预计阅读本页时间:-
Class Properties
A mechanism known as properties provides another way for new-style classes to define automatically called methods for access or assignment to instance attributes. At least for specific attributes, this feature is an alternative to many current uses of the __getattr__ and __setattr__ overloading methods we studied in Chapter 29. Properties have a similar effect to these two methods, but they incur an extra method call for any accesses to names that require dynamic computation. Properties (and slots) are based on a new notion of attribute descriptors, which is too advanced for us to cover here.
In short, a property is a type of object assigned to a class attribute name. A property is generated by calling the property built-in with three methods (handlers for get, set, and delete operations), as well as a docstring; if any argument is passed as None or omitted, that operation is not supported. Properties are typically assigned at the top level of a class statement [e.g., name = property(...)]. When thus assigned, accesses to the class attribute itself (e.g., obj.name) are automatically routed to one of the accessor methods passed into the property. For example, the __getattr__ method allows classes to intercept undefined attribute references:
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
>>> class classic:
... def __getattr__(self, name):
... if name == 'age':
... return 40
... else:
... raise AttributeError
...
>>> x = classic()
>>> x.age # Runs __getattr__
40
>>> x.name # Runs __getattr__
AttributeError
Here is the same example, coded with properties instead (note that properties are available for all classes but require the new-style object derivation in 2.6 to work properly for intercepting attribute assignments):
>>> class newprops(object):
... def getage(self):
... return 40
... age = property(getage, None, None, None) # get, set, del, docs
...
>>> x = newprops()
>>> x.age # Runs getage
40
>>> x.name # Normal fetch
AttributeError: newprops instance has no attribute 'name'
For some coding tasks, properties can be less complex and quicker to run than the traditional techniques. For example, when we add attribute assignment support, properties become more attractive—there’s less code to type, and no extra method calls are incurred for assignments to attributes we don’t wish to compute dynamically:
>>> class newprops(object):
... def getage(self):
... return 40
... def setage(self, value):
... print('set age:', value)
... self._age = value
... age = property(getage, setage, None, None)
...
>>> x = newprops()
>>> x.age # Runs getage
40
>>> x.age = 42 # Runs setage
set age: 42
>>> x._age # Normal fetch; no getage call
42
>>> x.job = 'trainer' # Normal assign; no setage call
>>> x.job # Normal fetch; no getage call
'trainer'
The equivalent classic class incurs extra method calls for assignments to attributes not being managed and needs to route attribute assignments through the attribute dictionary (or, for new-style classes, to the object superclass’s __setattr__) to avoid loops:
>>> class classic:
... def __getattr__(self, name): # On undefined reference
... if name == 'age':
... return 40
... else:
... raise AttributeError
... def __setattr__(self, name, value): # On all assignments
... print('set:', name, value)
... if name == 'age':
... self.__dict__['_age'] = value
... else:
... self.__dict__[name] = value
...
>>> x = classic()
>>> x.age # Runs __getattr__
40
>>> x.age = 41 # Runs __setattr__
set: age 41
>>> x._age # Defined: no __getattr__ call
41
>>> x.job = 'trainer' # Runs __setattr__ again
>>> x.job # Defined: no __getattr__ call
Properties seem like a win for this simple example. However, some applications of __getattr__ and __setattr__ may still require more dynamic or generic interfaces than properties directly provide. For example, in many cases, the set of attributes to be supported cannot be determined when the class is coded, and may not even exist in any tangible form (e.g., when delegating arbitrary method references to a wrapped/embedded object generically). In such cases, a generic __getattr__ or a __setattr__ attribute handler with a passed-in attribute name may be preferable. Because such generic handlers can also handle simpler cases, properties are often an optional extension.
For more details on both options, stay tuned for Chapter 37 in the final part of this book. As we’ll see there, it’s also possible to code properties using function decorator syntax, a topic introduced later in this chapter.