给Python对象添加新属性?

0 投票
3 回答
2052 浏览
提问于 2025-04-18 01:23

我来自面向对象编程的背景,所以看到下面的代码有点奇怪,来自这个链接

def f():
    f.beencalled = True
    return 0

我的问题是:

1) 从上面的代码来看,f 是一个引用变量,它指向一个名为 fclass 'function' 对象吗?

2) 我们给对象 f 添加了一个新的属性 beencalled,所以现在 'function' 类并没有定义这个属性 beencalled,那么我们还可以说对象 fclass 'function' 的对象吗?这样说有道理吗?

3 个回答

1

f() 只是 types.FunctionType 的一个实例,而实例可以有自己的属性。

给一个实例添加属性不会影响它的类,除非你重写了这个类的 __setattr__ 方法,并在里面做了一些不好的事情。

>>> import types
>>> def func(): pass
>>> isinstance(func, types.FunctionType)
True
5

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++中。我决定既然要支持继承,就顺便支持一个简单的多重继承版本。因此,每个类对象可以有一个或多个基类。

在这个实现中,处理对象的底层机制其实非常简单。每当对实例变量或类变量进行更改时,这些更改会直接反映在底层的字典对象中。例如,给一个实例设置实例变量会更新它的本地实例字典。同样,当查找一个对象的实例变量的值时,只需检查它的实例字典是否存在该变量。如果在这里找不到该变量,事情就变得有趣了。在这种情况下,会在类字典中查找,然后在每个基类的类字典中查找。

2

稍微换个话题,你可以为自定义类改变这种行为。

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'}

撰写回答