TrashPanda Wiki
No edit summary
No edit summary
Line 88: Line 88:
 
 
 
'''@staticmethod'''
 
'''@staticmethod'''
def validate_topping(topping):
+
def validate_topping(topping): # <- See! No 'self' or 'cls' parameters!
 
if topping == "pineapple":
 
if topping == "pineapple":
 
raise ValueError("No pineapples!")
 
raise ValueError("No pineapples!")

Revision as of 21:15, 20 May 2019

Object Life-Cycle

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 Hiding

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 Methods

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 Methods

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.