Everything is a object in Python.
the simplest class in Python:
Class and Instance Attributes
- Class attributes are attributes that are owned by the class itself.
- Instance attributes are owned by the specific instances of a class
Dynamically created attributes for existing instances of a class – not the way to properly create instance attributes, but an option.
Python checks first, if “brand” is a key of the y.__dict__ dictionary (instance attribute). If it is not, Python checks if “brand” is a key to the Robot.__dict__ (class attribute). If so, the value can be retrieved. => we can access a class attribute via an instance or via the class name.
Remove instance attribute: del x.year
Check which class the instance belongs to: <instance>.__class__
A method is “just” a function that is defined inside of a class.
__init__ is a method that is immediately and automatically called after an instance has been created and used to initialize an instance.
__str__ and __repr__
Using these methods is the Pythonic way to control how objects are converted to strings in different situations
If you apply str or repr to an object, Python is looking for a corresponding method __str__ or __repr__ in the class definition of the object. If the method does exist, it will be called.
- __str__ – if the output should be for the end user (nicely printed)
- __repr__ – for the internal representation of an object (like in a Python interpreter session)
- lists and dicts always use the result of repr
If would like to return the name by typing just an instance – use __str__ method:
If there is no __str__ method, Python falls back to __repr__ when looking for __str__ => it’s recommended to always add at least a __repr__ method to classes.
A proper way to define attributes (using __init__) and method say_hi (inside the class):
Python uses a special naming for attributes to control the accessibility of the attributes. Public- Protected- and Private Attributes:
|name||Public||These attributes can be freely used inside or outside of a class definition.|
|_name||Protected||Protected attributes should not be used outside of the class definition, unless inside of a subclass definition.|
|__name||Private||This kind of attribute is inaccessible and invisible. It’s neither possible to read nor write to those attributes, except inside of the class definition itself.|
Class methods are not bound to instances but are bound to a class.
The first parameter of a class method is a reference to a class, not the instance for instance methods. And return the instance of the class based on the results.
send a string=user_input into the class method “from_string”. It checks desc and date from the string using functions and returns the class element Element (return cls(desc, date) – into __init__. When we call just event class, it returns the string because of __str__.
Static methods don’t need a reference to an instance and could be called via the class name or instance name.
Just a way to organize the code. is_age_valid could be just a function not inside the class:
If we need to check or do something with the attribute, make an instance attribute – private and use the property to work with attributes. The main purpose of any decorator is to change your class methods or attributes in such a way so that the user of your class no need to make any change in their code.
A property object has three methods, getter(), setter(), and deleter() to specify fget, fset and fdel at a later point.
class Celsius: def __init__(self, temperature = 0): self._temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 @property def temperature(self): print("Getting value") return self._temperature @temperature.setter def temperature(self, value): if value < -273: raise ValueError("Temperature below -273 is not possible") print("Setting value") self._temperature = value
Or if just to make something with attributes. Just writing @property above the function power() make it available to be used as a property.
class Robot: def __init__(self, power): self._power = power power = property() @power.setter def power(self, power): print('Setter') self._power = power @power.getter def power(self): print('Getter') return self._power @power.deleter def power(self): del self._power x = Robot(200) print(x.power) x.power = 300 print(x.power)
Sometimes own error types can be very helpful. It allows you to define the meaningful name that makes the code easier to understand and to debug.
vs with just ValueError: