FANDOM


 
Line 84: Line 84:
 
They are marked with the '''staticmethod decorator'''.
 
They are marked with the '''staticmethod decorator'''.
 
class Pizza:
 
class Pizza:
def __init__(self, toppings):
+
def __init__(self, toppings):
self.toppings = toppings
+
self.toppings = toppings
 
 
'''@staticmethod'''
+
''' @staticmethod'''
def validate_topping(topping): # <- See! No 'self' or 'cls' parameters!
+
def validate_topping(topping): # <- See! No 'self' or 'cls' parameters!
if topping == "pineapple":
+
if topping == "pineapple":
raise ValueError("No pineapples!")
+
raise ValueError("No pineapples!")
else:
+
else:
return True
+
return True
 
 
 
ingredients = ["cheese", "onions", "spam"]
 
ingredients = ["cheese", "onions", "spam"]
Line 98: Line 98:
 
pizza = Pizza(ingredients)
 
pizza = Pizza(ingredients)
 
Static methods behave like plain functions, except for the fact that '''you can call them from an instance of the class'''.
 
Static methods behave like plain functions, except for the fact that '''you can call them from an instance of the class'''.
  +
  +
  +
==Properties==
  +
'''Properties '''provide a way of customizing access to instance attributes.
  +
  +
They are created by putting the '''property decorator '''above a method, which means when the instance attribute with the same name as the method is accessed, the method will be called instead.
  +
  +
One common use of a property is to make an attribute '''read-only'''.
  +
class Pizza:
  +
def __init__(self, toppings):
  +
self.toppings = toppings
  +
  +
''' @property'''
  +
def pineapple_allowed(self):
  +
return False
  +
  +
pizza = Pizza(["cheese", "tomato"])
  +
print(pizza.pineapple_allowed)
  +
pizza.pineapple_allowed = True
  +
  +
  +
>>>
  +
False
  +
  +
AttributeError: can't set attribute
  +
>>>
  +
Properties can also be set by defining '''setter/getter '''functions.
  +
  +
The '''setter '''function sets the corresponding property's value.
  +
  +
The '''getter '''gets the value.
  +
  +
To define a '''setter:'''
  +
@<property name>.setter
  +
Same applies for a '''getter '''function.
  +
@pineapple_allowed.setter
  +
    def pineapple_allowed(self, value):
  +
        if value:
  +
            password = input("Enter the password: ")
  +
            if password == "Sw0rdf1sh!":
  +
                self._pineapple_allowed = value
  +
            else:
  +
                raise ValueError("Alert! Intruder!")
  +
  +
print(pizza.pineapple_allowed)
  +
  +
  +
>>>
  +
False
  +
Enter the password: Sw0rdf1sh!
  +
True

Latest revision as of 19:25, May 21, 2019

Object Life-Cycle Edit

The lifecycle of an object mis made up of it's creationmanipulation, and destruction.

The stages of the object's lifecycle look like this:

  1. Definition - The first stage, the definition of the class to which it belongs.
  2. Instantiation - When __init__ is called. Memory is allocated to store the instance.
  3. "New" method - The __new__ method is called. This is usually overridden only in special cases.
  4. Object ready - Now the object is ready to be used.


When an object is destroyed, the memory allocated to it is freed up, and can be used for other purposes. An object can be deleted using the del keyword.

Data HidingEdit

A key part of OOP is encapsulation.

Encapsulation is the packaging of related variables and funcitons into a single easy-to-use object - an instance of a class.

A related concept to this is data hiding.

Data hiding states that implementation details of a class should be hidden, and a clean standard interface be presented for users of the class.

In Python, there are no ways of enforcing a method or attribute to be private (much unlike a language like C++). However there are ways to discourage people from accessing parts of a class, such as by denoting that it is an implementation detail, and should be used at own risk.

Weakly private methods and attributes have a single underscore at the beginning. This signals that they are private, and shouldn't br used by external code. However this is just a convention and does not stop external code for accessing them. It's only actual effect is that from module_name import * won't import variables that start with a single underscore.

Strongly private methods and attributes have a double underscore at the beginning of their names. This causes their names to be "mangled", which means that they can't be accessed from outside the class. The purpose of this isn't to ensure that they are kept private, but to avoid bugs if there are subclasses that have methods or attributes with the same names. "Name mangled" methods can still be accessed externally, but by a different name. 

# The method "__privatemethod" of class "Spam" could be accessed externally with 
# "_Spam_privatemethod"

class Spam:
    __egg = 7

    def print_egg(self):
        print(self.__egg)

s = Spam()
s.print_egg()
print(s._Spam__egg)
print(s.__egg)


>>>
7
7
AttributeError: 'Spam' object has no attribute '__egg'
>>>

So Python protects those members by internally changing the name to include the class name.


Class MethodsEdit

In most cases so far we have called object methods by an instance of a class, which is then passed to the self parameter of the method.

Class methods are different - they are called by a class, which is passed to the cls parameter of the method.

(Technically the parameters self and cls are just conventions; they could be chanaged to anything else. However, they are universally followed, so it is wise to stick to them)

A common use of these are factory methods, which instantiate an instance of a class, using different parameters than those usually passed to the class constructor.

Class methods are marked with a classmethod decorator.

class Rectangle:
  def __init__(self, width, height):
    self.width = width
    self.height = height

  def calculate_area(self):
    return self.width * self.height

  @classmethod
  def new_square(cls, side_length):
    return cls(side_length, side_length)

square = Rectangle.new_square(5) # <- See how the method is called on the class!
print(square.calculate_area())


>>>
25
>>>

new_squre is a class method and is called on the class, rather than on an instance of the class. It returns a new object of the class cls.

Static MethodsEdit

Static methods are similar to class methods, except they don't receive any additional arguments; they are identical to normal functions that belong to a class.

They are marked with the staticmethod decorator.

class Pizza:
    def __init__(self, toppings):
        self.toppings = toppings

    @staticmethod
    def validate_topping(topping): # <- See! No 'self' or 'cls' parameters!
        if topping == "pineapple":
            raise ValueError("No pineapples!")
        else:
            return True

ingredients = ["cheese", "onions", "spam"]
if all(Pizza.validate_topping(i) for i in ingredients):
pizza = Pizza(ingredients)

Static methods behave like plain functions, except for the fact that you can call them from an instance of the class.


PropertiesEdit

Properties provide a way of customizing access to instance attributes.

They are created by putting the property decorator above a method, which means when the instance attribute with the same name as the method is accessed, the method will be called instead.

One common use of a property is to make an attribute read-only.

class Pizza:
    def __init__(self, toppings):
        self.toppings = toppings

    @property
    def pineapple_allowed(self):
        return False

pizza = Pizza(["cheese", "tomato"])
print(pizza.pineapple_allowed)
pizza.pineapple_allowed = True


>>>
False

AttributeError: can't set attribute
>>>

Properties can also be set by defining setter/getter functions.

The setter function sets the corresponding property's value.

The getter gets the value.

To define a setter:

@<property name>.setter

Same applies for a getter function.

@pineapple_allowed.setter
    def pineapple_allowed(self, value):
        if value:
            password = input("Enter the password: ")
            if password == "Sw0rdf1sh!":
                self._pineapple_allowed = value
            else:
                raise ValueError("Alert! Intruder!")

print(pizza.pineapple_allowed)


>>>
False
Enter the password: Sw0rdf1sh!
True
Community content is available under CC-BY-SA unless otherwise noted.