Skip to content

反射

主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。

判断对象

用于获取对象的一些信息,判断对象的属性

len(s)

返回对象的长度(条目数量)。参数可以是一个序列(如 stringbytestuplelistrange)或集合(如字典,setfrozenset)。

python
>>> arr = [1, 2, 3]
>>> len(arr)
3

也可用于实现了 __len__() 方法的任意对象

python
>>> class A:
...     def __len__(self):
...         return 10
...
>>> a = A()
>>> len(a)
10

id(object)

返回一个对象的 identity 。它是一个整数,它在其生命周期中保证对这个对象唯一且恒定。具有非重叠生命周期的两个对象可能具有相同的 id() 值。

python
>>> arr = [1, 2, 3]
>>> id(arr)
2917306586176

CPython 实现细节:这是内存中对象的地址。

dir

python
dir([*object*])

尝试返回 object 的有效属性列表。如果没有参数,则返回当前本地作用域中的名称列表。

如果对象具有名为 __dir__() 的方法,则将调用此方法,并且必须返回属性列表。这允许实现自定义 __getattr__()__getattribute__() 函数的对象自定义 dir() 报告其属性。

默认的 dir() 机制对不同类型的对象有不同的表现,因为它试图产生最相关的信息,而不是完整的信息:

  • 如果对象是模块对象,则列表包含模块属性的名称。
  • 如果对象是一个类型或类对象,则该列表包含其属性的名称,并递归地显示其基础的属性。
  • 否则,该列表包含对象的属性名称,其类属性的名称以及其类的基类的属性的递归。

结果列表按字母顺序排序。例如:

python
>>> 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 关键字用于检查对象的类型

python
>>> type(list)
<class 'type'>

除此之外,还可以使用 type 定义类对象,那么就属于元类编程的范畴了。

python
class type(object)
class type(name, bases, dict)
  • 有一个参数时,返回 object 的类型。返回值是一个类型对象,通常与 object.__class__ 返回的对象相同。

    建议使用 isinstance() 内置函数来测试对象的类型,因为它会考虑子类。

  • 有三个参数时,返回一个新的类型对象。这实质上是类声明的一种动态形式。

    name 字符串是类名,并成为 __name__ 属性;

    bases 元组逐项列出基类,并成为 __bases__ 属性;

    dict 是包含类体的定义的命名空间,并被复制到标准字典中以变为 __dict__属性。

例如,以下两条语句会创建相同的类型对象:

python
>>> class X:
...     a = 1
...
>>> type(X)
<class 'type'>
python
>>> X = type('X', (object,), dict(a=1))
>>> type(X)
<class 'type'>

isinstance

python
isinstance(*object*, *classinfo*)

如果 object 参数是 classinfo 参数的实例或其(直接,间接或虚拟)子类的实例,则返回 true。如果 object 不是给定类型的对象,则该函数总是返回 false

如果 classinfo 是类型对象的元组, object 是其中任何一个类型的实例,则返回 true。如果 classinfo 不是类型或一组类型的元组,则会引发 TypeError 异常。

python
>>> isinstance(10, int)
True
>>> isinstance("str", (int, str))
True
>>> isinstance(max, int)
False

issubclass

python
issubclass(*class*, *classinfo*)

如果 classclassinfo 的子类(直接,间接或虚拟),则返回 true。一个类被认为是它自己的一个子类。

classinfo 可以是类对象的元组,在这种情况下,将检查 classinfo 中的每个条目。在任何其他情况下,都会引发 TypeError 异常。

python
>>> 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

python
getattr(object, name[, default])

返回 object 的指定属性的值。name 必须是字符串。如果字符串是 object 属性之一的名称,则结果是该属性的值。

例如,getattr(user, 'name') 等同于 user.name 。如果指定的属性不存在,则返回默认值(如果提供),否则引发 AttributeError

setattr

python
setattr(object, name, value)

它和 getattr() 是一对。参数是一个对象,一个字符串和一个任意值。该字符串可以是现有的属性名或新的属性名。

如果该对象允许,该函数将 value 分配给该属性。例如,setattr(user, 'age', 18) 等同于 user.age = 18

delattr

python
delattr(object, name)

参数是一个对象和一个字符串。该字符串必须是对象属性之一的名称。该函数删除指定的属性(只要该对象允许)。

例如, delattr(user, 'hobby') 等价于 del user.hobby

hasattr

python
hasattr(object, name)

参数是一个对象和一个字符串。如果字符串是 object 属性之一的名称,则结果为 True,否则为 False

这是通过调用 getattr(object, name) 并查看它是否引发 AttributeError 实现的。

属性操作案例

python
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 非常重要,经常用于判断对象是否包含某个属性。例如下面代码可能会报错

python
import random


class Example:
    pass


e = Example()
if random.choice([True, False]):
    e.name = '正心全栈编程'

print(e.name)

这种用法同样可能报错。

python
e = Example()
if random.choice([True, False]):
    e.name = '正心全栈编程'

if e.name:
    print('姓名为:', e.name)
else:
    print('对象没有设置姓名')

因为会先调用属性,然后再判断属性。调用的时候就报错了,自然无法判断。

下面这种用法才是正确的

python
if hasattr(e, 'name'):
    print('姓名为:', e.name)
else:
    print('对象没有设置姓名')

查找内容

globals()

返回表示当前全局符号表的字典。它总是当前模块的字典(在函数或方法内部,它是定义它的模块,而不是从中调用它的模块)。

python
>>> globals()
{
  '__name__': '__main__', 
  '__doc__': None, 
  '__package__': None, 
  '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 
  '__spec__': None, 
  '__annotations__': {}, 
  '__builtins__': <module 'builtins' (built-in)>
}

我对输出内容进行了格式化,输出的就是 Python 运行时依赖的东西。如果我们在代码中导入了模块、定义了变量都会出现在其中。

locals()

更新并返回表示当前局部环境变量的字典。在函数块中调用时,locals() 返回函数内部定义的变量,但不能在类块中调用。

不应该修改其中的内容;更改可能不会影响解释器使用的本地变量和局部变量的值。

python
>>> 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 字典的更新会被忽略。

python
>>> 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 是一个变量名列表, 获取所有变量名的字符串用于构造字典.

python
{'变量':'变量',}

使用一下内容

python
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'}
参考答案
python
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)