Python魔术方法之『__dict__』

在这篇文章中,我们一起看一下Python提供的内置魔法方法__dict__ 你将会明白到什么是 __dict__ ,以及如何在代码中使用它,它与dir()函数有什么不同

__dict__’ 在 Python 中的角色?

Python 使用一个特殊的内置属性 __dict__ 来存储对象的可变属性

通过object.__dict__可以得到一个字典,包含着属性的『键/值』对

__dict__ 属性不仅限于实例对象,也可以用于用户定义的函数、模块、类 (除非声明了 __slot__

看看下面的例子:

class Employee:
	def __init__(self, name, age):
		self.name = name
		self.age = age

employee1 = Employee('John', 23)
employee2 = Employee('Rambo', 28)

print("The output of employee1.__dict__ is : ", employee1.__dict__)
print("The output of employee2.__dict__ is : ", employee2.__dict__) 

__dict__ 分别把每一个 employeel 对象的属性,以字典的形式返回了

The output of employee1.dict is : {'name': 'John', 'age': 23}
The output of employee2.dict is : {'name': 'Rambo', 'age': 28}

如果类本身,没有任何属性,也不要求实例化过程中,绑定属性值

class A:
	pass

a = A()
print(a.__dict__) 
>>> {}

对象 a.__dict__,会返回一个空字典

如何使用__dict__来检索属性值?

__dict__返回的内容是字典,因此我们可以使用字典获取键值的常规方法,来获得对象的任何属性值。

class A:
	pass

a = A()
print(type(a.__dict__))

Output:
>>> <class 'dict'>

为了获得 person 对象的name 属性的值,可以按照下面的例子获取到

class Person:
	def __init__(self, name):
		self.name = name

person = Person('John') 
person.__dict__['name'] 

Output:
>>> John

如何使用__dict__来设置属性值?

我们也可以直接通过 __dict__ 来给对象的属性赋值

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

a = A(1)
print(a.x)
>>>1

a.__dict__['x'] = 2
print(a.x)
>>> 2

如何使用__dict__来添加新的属性?

既然可以修改,也能够添加一个不存在的属性值

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

a = A(1)
a.__dict__['y'] = 2

print(a.y) 
>>> 2

让我们验证一下,刚刚增添的属性 y 是否正确的和对象 a 绑定在了一起:

print(a.__dict__) 
>>> {'y': 2, 'x': 1}

如何使用__dict__来删除属性??

当我们想要删除某个属性,通过 del 就可以了:

class A:
	def __init__(self, x, y):
		self.x = x
		self.y = y

a = A(1, 2)
print("before deleting:")
print(a.__dict__)

del a.__dict__['y']
print("after deleting:")
print(a.__dict__) 

Output:
before deleting:
{'y': 2, 'x': 1}
after deleting:
{'x': 1}

函数调用__dcit__

The __dict__ attribute is also available to be used for normal functions(declared using ‘def’) and lambda.

__dict__ 方法也可以作用于普通的函数、lambda 匿名函数

def foobar():
	pass

print(foobar.__dict__) 
>>> {}

The __dict__, in this case, contains the function attributes .

So, we can do something like this

foobar.x = 1
foobar.__dict__
>>> {'x': 1}

我们也可通过 __dict__ 访问函数的『属性』x的值

foobar.__dict__['x']
>>> 1

类调用__dcit__

We can also access class methods and class variables using the __dict__ attribute.

我们也可以通过 __dict__ 属性,直接访问类本身的方法和类属性

class Foobar:
	pass

print(Foobar.__dict__) 

Output:

{'__dict__': <attribute '__dict__' of 'Foobar' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Foobar' objects>}

Foobar 中,定义类属性class_var,和一个类方法cls_method

class Foobar:
	class_var = 1
	@classmethod
	def cls_method(cls, name):
		pass

print(Foobar.__dict__) 

Output:

{'__dict__': <attribute '__dict__' of 'Foobar' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Foobar' objects>,
              'class_var': 1,
              'cls_method': <classmethod object at 0x000002815DCCBA30>}

模块调用__dcit__

任何时候,例如:如果我们A.py文件中,把 B文件作为模块导入

然后访问 B 文件中的一个变量的值,像这样:

# 假设这是 A.py 文件,B.py 文件中含有一个变量 x = None
import B
B.x = 1

实际上,python 悄悄地调用(隐式调用)了 B.__dict__[x]=1,来实现对模块 B 中变量 x 的赋值操作

__dict__ & dir 的区别

dir() 函数可以列出对象、对象可用所属的类、父类的所有属性和方法

class Fruit:
	cls_var = 1

	def __init__(self, name):
		self.name = name

	def something(self):
		pass

f = Fruit('Apple')
print("n dir(f) contains:")
print(dir(f))
print("n f.__dict__ contains:")
print(f.__dict__) 

Output:

>>> dir(f) contains:
>>>
['__class__','__delattr__',
 '__dict__','__dir__','__doc__',
 '__eq__', '__format__',
 '__ge__','__getattribute__',
 '__gt__','__hash__',
 '__init__','__init_subclass__',
 '__le__','__lt__',
 '__module__','__ne__',
 '__new__','__reduce__',
 '__reduce_ex__','__repr__',
 '__setattr__','__sizeof__',
 '__str__','__subclasshook__',
 '__weakref__','cls_var','name',]

__dict__ 只包含对象的局部属性

>>> f.__dict__ contains:
>>> {'name': 'Apple'}

如果要获取类的变量值,通过 类名.__dict__就可获取到了

print(Fruit.__dict__) 

Output:

{'__dict__': <attribute '__dict__' of 'Fruit' objects>,
              '__doc__': None,
              '__init__': <function Fruit.__init__ at 0x000002815DC70310>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Fruit' objects>,
              'cls_var': 1,
              'something': <function Fruit.something at 0x000002815E0AFDC0>}

相较于 dir() 只能访问,__dict__能够做到修改和删除对象的属性值

print(f.__dict__)
f.__dict__['name'] = 'Orange'
print(f.__dict__) 

>>> {'name': 'Apple'}
>>> {'name': 'Orange'}

del f.__dict__['name']
print(f.__dict__) 

>>> {}

__dict__并不能访问一切对象的属性,例如:

  1. 内建的数据类型
  2. 对象的类中的属性,被赋值给了 __slots__ 变量
>>> a = [1,2]
>>> a.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__dict__' 
>>> dir(a)
['__add__','__class__','__contains__',
 '__delattr__','__delitem__',
 '__dir__','__doc__','__eq__',
 '__format__','__ge__','__getattribute__',
 '__getitem__','__gt__','__hash__',
 '__iadd__','__imul__','__init__',
 '__init_subclass__','__iter__','__le__',
 '__len__','__lt__','__mul__','__ne__','__new__',
 '__reduce__','__reduce_ex__',
 '__repr__','__reversed__','__rmul__',
 '__setattr__','__setitem__','__sizeof__',
 '__str__','__subclasshook__',
 'append','clear','copy','count',
 'extend','index','insert','pop','remove','reverse',
 'sort']

实例的属性值,被赋值给了 __slots__变量

class Person:
	__slots__ = ['name', 'age']

	def __init__(self, name, age):
		self.name = name
		self.age = age

p = Person('Jason', 34)
p.__dict__ 

Output:

Traceback (most recent call last):
  File "person.py", line 11, in <module>
    p.__dict__
AttributeError: 'Person' object has no attribute '__dict__

Output:

print(dir(p)) 
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'age', 'name']

参考

尝试翻译的这篇英语原文,提升自己的英语阅读能力XD

  1. Understanding ‘dict’ in Python