反射
主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
判断对象
用于获取对象的一些信息,判断对象的属性
len(s)
返回对象的长度(条目数量)。参数可以是一个序列(如 string
,bytes
,tuple
,list
或 range
)或集合(如字典,set
或 frozenset
)。
>>> arr = [1, 2, 3]
>>> len(arr)
3
也可用于实现了 __len__()
方法的任意对象
>>> class A:
... def __len__(self):
... return 10
...
>>> a = A()
>>> len(a)
10
id(object)
返回一个对象的 identity
。它是一个整数,它在其生命周期中保证对这个对象唯一且恒定。具有非重叠生命周期的两个对象可能具有相同的 id()
值。
>>> arr = [1, 2, 3]
>>> id(arr)
2917306586176
CPython 实现细节:这是内存中对象的地址。
dir
dir([*object*])
尝试返回 object
的有效属性列表。如果没有参数,则返回当前本地作用域中的名称列表。
如果对象具有名为 __dir__()
的方法,则将调用此方法,并且必须返回属性列表。这允许实现自定义 __getattr__()
或 __getattribute__()
函数的对象自定义 dir()
报告其属性。
默认的 dir()
机制对不同类型的对象有不同的表现,因为它试图产生最相关的信息,而不是完整的信息:
- 如果对象是模块对象,则列表包含模块属性的名称。
- 如果对象是一个类型或类对象,则该列表包含其属性的名称,并递归地显示其基础的属性。
- 否则,该列表包含对象的属性名称,其类属性的名称以及其类的基类的属性的递归。
结果列表按字母顺序排序。例如:
>>> dir(list)
['__add__', '__class__', '__class_getitem__', '__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']
>>> class Shape:
... def __dir__(self):
... return ['area', 'perimeter', 'location']
...
>>> s = Shape()
>>> dir(s)
['area', 'location', 'perimeter']
type
type
关键字用于检查对象的类型
>>> type(list)
<class 'type'>
除此之外,还可以使用 type
定义类对象,那么就属于元类编程的范畴了。
class type(object)
class type(name, bases, dict)
有一个参数时,返回
object
的类型。返回值是一个类型对象,通常与object.__class__
返回的对象相同。建议使用
isinstance()
内置函数来测试对象的类型,因为它会考虑子类。有三个参数时,返回一个新的类型对象。这实质上是类声明的一种动态形式。
name
字符串是类名,并成为__name__
属性;bases
元组逐项列出基类,并成为__bases__
属性;dict
是包含类体的定义的命名空间,并被复制到标准字典中以变为__dict__
属性。
例如,以下两条语句会创建相同的类型对象:
>>> class X:
... a = 1
...
>>> type(X)
<class 'type'>
>>> X = type('X', (object,), dict(a=1))
>>> type(X)
<class 'type'>
isinstance
isinstance(*object*, *classinfo*)
如果 object
参数是 classinfo
参数的实例或其(直接,间接或虚拟)子类的实例,则返回 true
。如果 object
不是给定类型的对象,则该函数总是返回 false
。
如果 classinfo
是类型对象的元组, object
是其中任何一个类型的实例,则返回 true
。如果 classinfo
不是类型或一组类型的元组,则会引发 TypeError
异常。
>>> isinstance(10, int)
True
>>> isinstance("str", (int, str))
True
>>> isinstance(max, int)
False
issubclass
issubclass(*class*, *classinfo*)
如果 class
是 classinfo
的子类(直接,间接或虚拟),则返回 true
。一个类被认为是它自己的一个子类。
classinfo
可以是类对象的元组,在这种情况下,将检查 classinfo
中的每个条目。在任何其他情况下,都会引发 TypeError
异常。
>>> issubclass(int, int)
True
>>> issubclass(int, str)
False
>>> issubclass(10, int)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: issubclass() arg 1 must be a class
操作属性
getattr
getattr(object, name[, default])
返回 object
的指定属性的值。name
必须是字符串。如果字符串是 object
属性之一的名称,则结果是该属性的值。
例如,getattr(user, 'name')
等同于 user.name
。如果指定的属性不存在,则返回默认值(如果提供),否则引发 AttributeError
。
setattr
setattr(object, name, value)
它和 getattr()
是一对。参数是一个对象,一个字符串和一个任意值。该字符串可以是现有的属性名或新的属性名。
如果该对象允许,该函数将 value
分配给该属性。例如,setattr(user, 'age', 18)
等同于 user.age = 18
。
delattr
delattr(object, name)
参数是一个对象和一个字符串。该字符串必须是对象属性之一的名称。该函数删除指定的属性(只要该对象允许)。
例如, delattr(user, 'hobby')
等价于 del user.hobby
。
hasattr
hasattr(object, name)
参数是一个对象和一个字符串。如果字符串是 object
属性之一的名称,则结果为 True
,否则为 False
。
这是通过调用 getattr(object, name)
并查看它是否引发 AttributeError
实现的。
属性操作案例
class User:
def __init__(self, name):
self.name = name
user = User('正心')
setattr(user, 'age', 18) # 等价于 user.age = 18
setattr(user, 'hobby', ['吃饭', '睡觉', '写代码'])
print(getattr(user, 'hobby')) # 等价于 print(user.hobby)
delattr(user, 'hobby') # 等价于 del user.hobby
print(hasattr(user, 'hobby')) # 判断 user 是否有 hobby 属性
提示
hasattr 非常重要,经常用于判断对象是否包含某个属性。例如下面代码可能会报错
import random
class Example:
pass
e = Example()
if random.choice([True, False]):
e.name = '正心全栈编程'
print(e.name)
这种用法同样可能报错。
e = Example()
if random.choice([True, False]):
e.name = '正心全栈编程'
if e.name:
print('姓名为:', e.name)
else:
print('对象没有设置姓名')
因为会先调用属性,然后再判断属性。调用的时候就报错了,自然无法判断。
下面这种用法才是正确的
if hasattr(e, 'name'):
print('姓名为:', e.name)
else:
print('对象没有设置姓名')
查找内容
globals()
返回表示当前全局符号表的字典。它总是当前模块的字典(在函数或方法内部,它是定义它的模块,而不是从中调用它的模块)。
>>> globals()
{
'__name__': '__main__',
'__doc__': None,
'__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>
}
我对输出内容进行了格式化,输出的就是 Python 运行时依赖的东西。如果我们在代码中导入了模块、定义了变量都会出现在其中。
locals()
更新并返回表示当前局部环境变量的字典。在函数块中调用时,locals()
返回函数内部定义的变量,但不能在类块中调用。
不应该修改其中的内容;更改可能不会影响解释器使用的本地变量和局部变量的值。
>>> def local_var():
... name = '正心'
... age = 10
... def func():
... pass
... print('局部变量', locals())
...
>>>
>>> local_var()
局部变量 {'name': '正心', 'age': 10, 'func': <function local_var.<locals>.func at 0x00000273C05C3E20>}
vars([object])
返回一个模块、字典、类、实例或者其它任何一个具有 __dict__
属性的对象的 __dict__
属性。
模块和实例这样的对象的 __dict__
属性可以更新;但是其它对象可能对它们的 __dict__
属性的写操作有限制(例如,类使用 types.MappingProxyType
来阻止对字典直接更新)。
如果不带参数,vars()
的行为就像 locals()
。注意,locals
字典只用于读取,因为对 locals
字典的更新会被忽略。
>>> class Person:
... name = "John"
... age = 36
... country = "norway"
...
>>>
>>> x = vars(Person)
>>> x
mappingproxy({'__module__': '__main__', 'name': 'John', 'age': 36, 'country': 'norway', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None})
>>> {k:v for k,v in x.items() if not k.startswith('__')}
{'name': 'John', 'age': 36, 'country': 'norway'}
案例演示(了解)
假设 names
是一个变量名列表, 获取所有变量名的字符串用于构造字典.
{'变量':'变量',}
使用一下内容
a, b, c, d, e = ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
names = [a, b, c, d, e]
构建
{'a': 'aaa', 'b': 'bbb', 'c': 'ccc', 'd': 'ddd', 'e': 'eee'}
参考答案
a, b, c, d, e = ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
names = [a, b, c, d, e]
# 利用 locals 获取所有的变量名
vk = {}
# 获取局部变量副本 保证不修改局部变量
p_locals = locals().copy()
print('p_locals', p_locals)
# 将内容变为键值对, 并获取id与对象的隐射关系
# k 是变量的字符串
for k, v in p_locals.items():
vk[id(v)] = k
print("vk", vk)
data_dict = {}
# 获取变量名的字符串
for i in names:
data_dict.update({
vk[id(i)]: i
})
# 打印字符串
print(data_dict)