Normally, when we define a class and create an instance of class, we can bind any property and method to the instance, which is the flexibility of dynamic language. First define class:
class Student(object): pass
Then, try to bind a property to the instance:
>>> s = Student() >>>S.name ='michael '# dynamically bind an attribute to the instance >>> print(s.name) Michael
You can also try to bind a method to the instance:
>>> def set_ Age (self, age): # define a function as an instance method ... self.age = age ... >>> from types import MethodType >>> s.set_ age = MethodType(set_ Age, s) # bind a method to an instance >>> s.set_ Age (25) # call instance method >>>S.age # test results 25
However, the method of binding one instance does not work for another instance
>>>S2 = student () # create a new instance >>> s2.set_ Age (25) # trying to call a method Traceback (most recent call last): File "", line 1, in AttributeError: 'Student' object has no attribute 'set_age'
To bind methods to all instances, you can bind methods to class:
>>> def set_score(self, score): ... self.score = score ... >>> Student.set_score = set_score
After the class binding method is given, all instances can call:
>>> s.set_score(100) >>> s.score 100 >>> s2.set_score(99) >>> s2.score 99
In general, the above set_ Score method can be directly defined in class, but dynamic binding allows us to dynamically add functions to class in the process of program running, which is difficult to implement in static language.
But what if we want to restrict the properties of an instance? For example, only the name and age attributes are allowed to be added to the student instance.
For the purpose of limitation, python allows you to define a special class when defining a class__ slots__ Variable to limit the attributes that the class instance can add
class Student(object): __ slots__ =('name ','age') defines the name of the attribute that is allowed to be bound with tuple
Then, let’s try:
>>>S = student () # create new instance >>>S.name ='michael '# binding attribute' name ' >>>S.age = 25 # binding attribute 'age' >>>S.score = 99 # binding attribute 'score' Traceback (most recent call last): File "", line 1, in AttributeError: 'Student' object has no attribute 'score'
Because ‘score’ is not put in__ slots__ So you cannot bind the score attribute. If you try to bind score, you will get an error of attributeerror.
Use__ slots__ Be careful__ slots__ The properties defined only work for the current class instance, but not for inherited subclasses
>>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999
Unless also defined in subclasses__ slots__ In this way, the properties that subclass instances are allowed to define are their own__ slots__ Add the parent’s__ slots__。
Using @ property
When Binding attributes, if we directly expose the attributes, although it’s very simple to write, we can’t check the parameters. As a result, we can change the scores randomly
s = Student() s.score = 9999
This is clearly illogical. In order to limit the scope of score, a set is used_ Score () method to set the score, and then through a get_ Score () to get the score_ In the score () method, you can check the parameters:
class Student(object): def get_score(self): return self._score def set_score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value
Now, if you operate on any student instance, you can’t set score at will
>>> s = Student() >>> s.set_score(60) # ok! >>> s.get_score() 60 >>> s.set_score(9999) Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!
However, the above call method is slightly more complex than using properties directly.
Is it possible to not only check parameters, but also access class variables in a simple way like properties? For the pursuit of perfect Python programmers, this is a must!
Remember that decorator can add functions dynamically? For class methods, decorators work as well. Python’s built-in @ property decorator is responsible for turning a method into a property call
class Student(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value
@The implementation of property is complex. Let’s first examine how to use it. To turn a getter method into a property, you just need to add @ property. At this time, @ property itself creates another [email protected] score.setter , which is responsible for turning a setter method into a property assignment. Therefore, we have a controllable property operation:
>>> s = Student() >>>S.score = 60 # OK, actually converted to s.set_ score(60) >>>S.score # OK, actually converted to s.get_ score() 60 >>> s.score = 9999 Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!
Notice this magical @ property. When we operate on instance properties, we know that the property is probably not directly exposed, but realized through getter and setter methods.
You can also define a read-only property. Only the getter method can be defined, and no setter method is a read-only property
class Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2015 - self._birth
The above birth is a read-write property, while age is a read-only property, because age can be calculated according to the birth and the current time.
@Property is widely used in the definition of class. It can make the callers write short code and ensure the necessary checking of parameters. In this way, the possibility of errors is reduced when the program is running.