在Python中,什么时候以及为什么要使用“self.__dict__”而不是“self.variable”?

1tuwyuhd  于 2022-11-19  发布在  Python
关注(0)|答案(3)|浏览(205)

我正在尝试理解一些代码,这些代码使用了下面的类:

class Base(object):

    def __init__(self, **kwargs):
        self.client = kwargs.get('client')
        self.request = kwargs.get('request')
    ...

    def to_dict(self):
        data = dict()

        for key in iter(self.__dict__): # <------------------------ this
            if key in ('client', 'request'):
                continue

            value = self.__dict__[key]
            if value is not None:
                if hasattr(value, 'to_dict'):
                    data[key] = value.to_dict()
                else:
                    data[key] = value
        return data

我知道它获取传递给Base类的关键字参数,例如Base(client="foo", request="bar")
我的困惑是,为什么要使用self.__dict____init__中的变量转换为dict(例如{"client": "foo", "request": "bar"}),而不是在其他方法中通过self.clientself.request调用它们?何时以及为什么应该使用self.__dict__

rkue9o1l

rkue9o1l1#

几乎在所有情况下,都不应该使用self.__dict__
如果你访问的是self.client这样的属性,也就是说,属性名是已知的并且是固定的,那么self.__dict__['client']self.__dict__['client']之间的唯一区别就是,如果示例中缺少属性,self.__dict__['client']不会在类中查找该属性。很少有理由这样做,但是区别如下所示:

>>> class A:
...     b = 3 # class attribute, not an instance attribute
... 
>>> A.b # the class has this attribute
3
>>> a = A()
>>> a.b # the instance doesn't have this attribute, fallback to the class
3
>>> a.__dict__['b'] # the instance doesn't have this attribute, but no fallback
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'b'

self.__dict__的主要用例是当你 * 不 * 想访问一个固定的、已知的属性名时。如果确实需要使用未知字符串动态查找某些内容,则应该自己创建一个字典,并编写self.that_dict[key]而不是self.__dict__[key]
因此,只有在编写需要工作的代码时,才应该真正使用__dict__,而不管示例可能具有哪些属性;也就是说,你特别想要的代码,即使你改变了类的结构或它的属性名,也能工作,或者代码能跨多个具有不同结构的类工作。

__repr__方法

__repr__方法的作用是返回一个代表示例的字符串,方便程序员使用REPL。为了调试/测试的目的,这个字符串通常包含对象状态的信息。下面是一个实现它的常用方法:

class Foo:
    def __init__(self, foo, bar, baz):
        self.foo = foo
        self.bar = bar
        self.baz = baz

    def __repr__(self):
        return 'Foo({!r}, {!r}, {!r})'.format(self.foo, self.bar, self.baz)

这意味着如果你写obj = Foo(1, 'y', True)来创建一个示例,那么repr(obj)将是字符串"Foo(1, 'y', True)",这很方便,因为它显示了示例的整个状态,而且字符串本身是Python代码,它创建了一个具有相同状态的示例。
但上述实现存在一些问题:如果类的属性改变了,我们就必须改变它,它不会为子类的示例给予有用的结果,而且我们必须为具有不同属性的不同类编写大量类似的代码。如果我们改用__dict__,我们就可以解决所有这些问题:

def __repr__(self):
        return '{}({})'.format(
            self.__class__.__name__,
            ', '.join('{}={!r}'.format(k, v) for k, v in self.__dict__.items())
        )

现在repr(obj)将是Foo(foo=1, bar='y', baz=True),它也显示了示例的整个状态,也是可执行的Python代码。如果Foo的结构发生变化,这个通用的__repr__方法仍然可以工作,它可以通过继承在多个类之间共享。并且它返回__init__接受其属性作为关键字参数的任何类的可执行Python代码。

l5tcr1uw

l5tcr1uw2#

__dict__保存类中的 * 所有 * 变量。

class A():
    def __init__(self, foo):
        self.foo = foo

    def new_var(self, bar):
        self.bar = bar

在这种情况下,请注意:

a = A('var1')
print(a.__dict__) # {'foo': 'var1'}

b = A('var1')
b.new_var('var2')
b.foobar = 'var3'
print(b.__dict__) # {'foo': 'var1', 'bar': 'var2', 'foobar': 'var3'}

__dict__是一个很好的方法,可以在调用该类的当前示例中获取属于该类的所有变量。您可以在此处查看__dict__的文档。

bwleehnv

bwleehnv3#

**dict**用于检查对象具有哪些示例变量(数据属性)。

所以,如果下面有Person类:

class Person:
    x1 = "Hello"
    x2 = "World"
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def test1(self):
        print("Test1")
        print(self.__dict__) # Here
        
    @classmethod
    def test2(cls):
        print("Test2")
    
    @staticmethod
    def test3():
        print("Test3")

person = Person("John", 27)    
person.test1() # Here

**dict**会取得nameage,其值位于字典中,如下所示:

Test1
{'name': 'John', 'age': 27} # Here

如果示例化后添加新示例变量gender,如下所示:

# ...

person = Person("John", 27)
person.test1()  
person.gender = "Male" # Here
person.test1()

**dict**会取得nameagegender,其值位于字典中,如下所示:

Test1
{'name': 'John', 'age': 27}
Test1
{'name': 'John', 'age': 27, 'gender': 'Male'} # Here

相关问题