预计阅读本页时间:-
The World’s Simplest Python Class
We’ve begun studying class statement syntax in detail in this chapter, but I’d again like to remind you that the basic inheritance model that classes produce is very simple—all it really involves is searching for attributes in trees of linked objects. In fact, we can create a class with nothing in it at all. The following statement makes a class with no attributes attached (an empty namespace object):
>>> class rec: pass # Empty namespace object
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
We need the no-operation pass statement (discussed in Chapter 13) here because we don’t have any methods to code. After we make the class by running this statement interactively, we can start attaching attributes to the class by assigning names to it completely outside of the original class statement:
>>> rec.name = 'Bob' # Just objects with attributes
>>> rec.age = 40
And, after we’ve created these attributes by assignment, we can fetch them with the usual syntax. When used this way, a class is roughly similar to a “struct” in C, or a “record” in Pascal. It’s basically an object with field names attached to it (we can do similar work with dictionary keys, but it requires extra characters):
>>> print(rec.name) # Like a C struct or a record
Bob
Notice that this works even though there are no instances of the class yet; classes are objects in their own right, even without instances. In fact, they are just self-contained namespaces, so as long as we have a reference to a class, we can set or change its attributes anytime we wish. Watch what happens when we do create two instances, though:
>>> x = rec() # Instances inherit class names
>>> y = rec()
These instances begin their lives as completely empty namespace objects. Because they remember the class from which they were made, though, they will obtain the attributes we attached to the class by inheritance:
>>> x.name, y.name # name is stored on the class only
('Bob', 'Bob')
Really, these instances have no attributes of their own; they simply fetch the name attribute from the class object where it is stored. If we do assign an attribute to an instance, though, it creates (or changes) the attribute in that object, and no other—attribute references kick off inheritance searches, but attribute assignments affect only the objects in which the assignments are made. Here, x gets its own name, but y still inherits the name attached to the class above it:
>>> x.name = 'Sue' # But assignment changes x only
>>> rec.name, x.name, y.name
('Bob', 'Sue', 'Bob')
In fact, as we’ll explore in more detail in Chapter 28, the attributes of a namespace object are usually implemented as dictionaries, and class inheritance trees are (generally speaking) just dictionaries with links to other dictionaries. If you know where to look, you can see this explicitly.
For example, the __dict__ attribute is the namespace dictionary for most class-based objects (some classes may also define attributes in __slots__, an advanced and seldom-used feature that we’ll study in Chapters 30 and 31). The following was run in Python 3.0; the order of names and set of __X__ internal names present can vary from release to release, but the names we assigned are present in all:
>>> rec.__dict__.keys()
['__module__', 'name', 'age', '__dict__', '__weakref__', '__doc__']
>>> list(x.__dict__.keys())
['name']
>>> list(y.__dict__.keys()) # list() not required in Python 2.6
[]
Here, the class’s namespace dictionary shows the name and age attributes we assigned to it, x has its own name, and y is still empty. Each instance has a link to its class for inheritance, though—it’s called __class__, if you want to inspect it:
>>> x.__class__
<class '__main__.rec'>
Classes also have a __bases__ attribute, which is a tuple of their superclasses:
>>> rec.__bases__ # () empty tuple in Python 2.6
(<class 'object'>,)
These two attributes are how class trees are literally represented in memory by Python.
The main point to take away from this look under the hood is that Python’s class model is extremely dynamic. Classes and instances are just namespace objects, with attributes created on the fly by assignment. Those assignments usually happen within the class statements you code, but they can occur anywhere you have a reference to one of the objects in the tree.
Even methods, normally created by a def nested in a class, can be created completely independently of any class object. The following, for example, defines a simple function outside of any class that takes one argument:
>>> def upperName(self):
... return self.name.upper() # Still needs a self
There is nothing about a class here yet—it’s a simple function, and it can be called as such at this point, provided we pass in an object with a name attribute (the name self does not make this special in any way):
>>> upperName(x) # Call as a simple function
'SUE'
If we assign this simple function to an attribute of our class, though, it becomes a method, callable through any instance (as well as through the class name itself, as long as we pass in an instance manually):[61]
>>> rec.method = upperName
>>> x.method() # Run method to process x
'SUE'
>>> y.method() # Same, but pass y to self
'BOB'
>>> rec.method(x) # Can call through instance or class
'SUE'
Normally, classes are filled out by class statements, and instance attributes are created by assignments to self attributes in method functions. The point again, though, is that they don’t have to be; OOP in Python really is mostly about looking up attributes in linked namespace objects.