给Python对象添加新属性?
我来自面向对象编程的背景,所以看到下面的代码有点奇怪,来自这个链接
def f():
f.beencalled = True
return 0
我的问题是:
1) 从上面的代码来看,f
是一个引用变量,它指向一个名为 f
的 class 'function'
对象吗?
2) 我们给对象 f
添加了一个新的属性 beencalled
,所以现在 'function'
类并没有定义这个属性 beencalled
,那么我们还可以说对象 f
是 class 'function'
的对象吗?这样说有道理吗?
3 个回答
f()
只是 types.FunctionType
的一个实例,而实例可以有自己的属性。
给一个实例添加属性不会影响它的类,除非你重写了这个类的 __setattr__
方法,并在里面做了一些不好的事情。
>>> import types
>>> def func(): pass
>>> isinstance(func, types.FunctionType)
True
1) 是的:
>>> def f():
print(type(f))
>>> f()
>>> <class 'function'>
2) function
这个类本身没有新的属性,但对象f
有。给一个对象添加或删除属性不会影响同类其他对象的属性:
>>> class A: pass
>>> a = A()
>>> a.var = 7
>>> b = A()
>>> b.var
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
b.newvar
AttributeError: 'A' object has no attribute 'var'
在Python中,类比Java或C++要灵活得多。对象可以拥有类中没有定义的属性,甚至可以没有类中定义的属性!看看这个:
>>> class A:
def __init__(self, a):
self.var = a
>>> obj = A(7)
>>> del obj.var #deletes the var attribute from obj, does not change the A class
>>> obj.var
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
obj.var
AttributeError: 'A' object has no attribute 'var'
>>> obj2 = A(6)
>>> obj2.var #obj2 is a new object, so the fact we deleted var from obj doesn't affect it
6
编辑:经过一番搜索,我找到了解释为什么会有这种行为的原因(来源):
为了实现用户自定义对象,我选择了最简单的设计;一种方案是用一种新的内置对象来表示对象,这种对象存储了一个指向“类对象”的类引用,这个类对象是所有同类实例共享的,还有一个字典,称为“实例字典”,用来存放实例变量。
在这个实现中,实例字典会包含每个对象的实例变量,而类对象则包含所有同类实例共享的内容——特别是方法。在实现类对象时,我再次选择了最简单的设计;一个类的方法集合存储在一个字典中,字典的键是方法名。我称之为类字典。为了支持继承,类对象还会存储指向基类的类对象的引用。当时我对类的理解还比较幼稚,但我知道多重继承,最近刚刚被加入到C++中。我决定既然要支持继承,就顺便支持一个简单的多重继承版本。因此,每个类对象可以有一个或多个基类。
在这个实现中,处理对象的底层机制其实非常简单。每当对实例变量或类变量进行更改时,这些更改会直接反映在底层的字典对象中。例如,给一个实例设置实例变量会更新它的本地实例字典。同样,当查找一个对象的实例变量的值时,只需检查它的实例字典是否存在该变量。如果在这里找不到该变量,事情就变得有趣了。在这种情况下,会在类字典中查找,然后在每个基类的类字典中查找。
稍微换个话题,你可以为自定义类改变这种行为。
class FooBar(object):
__slots__ = ["foo","bar","baz"]
# if you don't define __slots__, you can add attr to the object as needed
# if you do, the object can only contain those attributes.
def __init__(self,foo=None,bar=None,baz=None):
self.foo = foo
self.bar = bar
self.baz = baz
def __str__(self):
return "I'm a FooBar with id {0} with foo: {1.foo}, bar: {1.bar}, baz: {1.baz}".format(id(self),self)
>>> a = FooBar("a","B","CCC")
>>> print(a)
I'm a FooBar with id 47260256 with foo: a, bar: B, baz: CCC
>>> a.spam = "eggs"
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
a.spam = "eggs"
AttributeError: 'FooBar' object has no attribute 'spam'
另外,如果不定义 __slots__
:
class BooFar(object):
def __str__(self):
return "I'm a BooFar with the following attributes:\n{}".format(self.__dict__)
>>> b = BooFar()
>>> print(b)
I'm a BooFar with the following attributes:
{}
>>> b.spam = "eggs"
>>> print(b)
I'm a BooFar with the following attributes:
{'spam': 'eggs'}