General Features of New-Style

Một phần của tài liệu Core python programming 2nd edition sep 2006 (Trang 632 - 635)

13.16 Advanced Features of New-Style

13.16.1 General Features of New-Style

We have already discussed some of the features tied to new-style classes.

With the unification of types and classes, the most significant of these fea- tures is the ability to subclass Python data types. One side effect of this is that all of the Python “casting” or conversion built-in functions are now factory functions. When you call them, you are really instantiating an instance of the corresponding type.

The following built-in function, which have been around Python for a while, have been quietly (or perhaps not) converted to factory functions:

• int(), long(), float(), complex()

• str(), unicode()

• list(), tuple()

• type()

ptg 596 Chapter 13 Object-Oriented Programming

In addition, several new ones have been added to round out the posse:

• basestring()1

• dict()

• bool()

• set(),2frozenset()2

• object()

• classmethod()

• staticmethod()

• super()

• property()

• file()

These class names and factory functions have flexible usage. In addition to creating new objects of those types, they can be used as base classes when subclassing types, and they can now be used with the isinstance() built-in function. Using is instance() can help replace tired old idioms with one that requires fewer functions calls resulting in cleaner code. For example, to test if an object is an integer, we had to call type() twice or import the types module and use its attributes; but now we can just use isinstance() and even gain in performance:

OLD (not as good):

if type(obj) == type(0)…

if type(obj) == types.IntType… BETTER:

if type(obj) is type(0)… EVEN BETTER:

if isinstance(obj, int)…

if isinstance(obj, (int, long))…

if type(obj) is int…

Keep in mind that although isinstance() is flexible, it does not perform an “exact match” comparison—it will also return True if obj is an instance of the given type or an instance of a subclass of the given type. You will still need to use the is operator if you want an exact class match.

1. New in Python 2.3.

2. New in Python 2.4.

ptg 13.16 Advanced Features of New-Style Classes (Python 2.2+) 597

Please review Section 13.12.2 above for a deeper explanation of isin- stance() as well as its introduction in Chapter 4 and how these calls evolved along with Python.

13.16.2 __slots__ Class Attribute

A dictionary is at the heart of all instances. The __dict__ attribute keeps track of all instance attributes. For example, when you have an instance inst with an attribute foo, recognize that accessing it with inst.foo is the same as doing it with inst.__dict__['foo'].

This dictionary takes up a good amount of memory, and if you have a class with very few attributes but a significant number of instances of this object, then you are taking a substantial hit. To combat this, users are now able to use the __slots__ attribute to substitute for __dict__.

Basically, __slots__ is a class variable consisting of a sequence-like object representing the set of valid identifiers that make up all of an instance’s attributes. This can be a list, tuple, or iterable. It can also be a single string identifying the single attribute that an instance can have. Any attempt to cre- ate an instance attribute with a name not in __slots__ will result in an AttributeError exception:

class SlottedClass(object):

__slots__ = ('foo', 'bar')

>>> c = SlottedClass()

>>>

>>> c.foo = 42

>>> c.xxx = "don't think so"

Traceback (most recent call last):

File "<stdin>", line 1, in ?

AttributeError: 'SlottedClass' object has no attribute 'xxx'

The primary reason for this feature is the conservation of memory. A side effect is a type of security preventing users from adding instances attributes dynamically in an ad hoc manner. A class defined with a __slots__ attribute will not have a __dict__ (unless you add '__dict__' as an element of __slots__). For more information on __slots__, see the Data Model chapter of the Python (Language) Reference Manual.

13.16.3 __getattribute__() Special Method

Python classes have a special method named __getattr__(), which is called only when an attribute cannot be found in an instance’s __dict__ or its

ptg 598 Chapter 13 Object-Oriented Programming

class (class’s __dict__), or ancestor class (its __dict__). One place where we saw __getattr__() used was for implementing delegation.

The problem that many users encountered was that they wanted a certain function to execute for every attribute access, not just when one cannot be found. This is where __getattribute__() comes in. It works just like __getattr__() except that it is always called when an attribute is accessed, not just when it cannot be found.

If a class has both __getattribute__() and __getattr__() defined, the latter will not be called unless explicitly called from __get- attribute__() or if __getattribute__() raises AttributeError.

Be very careful if you are going to access attributes in here… attributes of this class or an ancestor. If you cause __getattribute__() to somehow call __getattribute__()again, you will have infinite recursion. To avoid infi- nite recursion using this method, you should always call an ancestor class method that shares the same name in order to access any attributes it needs safely; for example, super(obj, self).__getattribute__(attr). This special method is only valid with new-style classes. As with __slots__, you can get more information on __getattribute__() by referring to the Data Model chapter of the Python (Language) Reference Manual.

Một phần của tài liệu Core python programming 2nd edition sep 2006 (Trang 632 - 635)

Tải bản đầy đủ (PDF)

(1.137 trang)