011. 如何在Python中动态创建类?
说在前面
答案是type
你印象中的type是用来查看对象的类型的
li = [] type(li) # 得到list
对自定义的类是这样的
class Person: pass wuxianfeng = Person() type(wuxianfeng) # __main__.Person
此处的wuxianfeng是一个Person类的实例
既然一切皆对象,那么Person的类型是啥呢?
type(Person) # type
去官网看看
class type(name, bases, dict, **kwds)
1. 传入一个参数时,返回 object 的类型。 返回值是一个 type 对象,通常与 object.__class__ 所返回的对象相同。
2. 推荐使用 isinstance() 内置函数来检测对象的类型,因为它会考虑子类的情况。
3. 传入三个参数时,返回一个新的 type 对象。 这在本质上是 class 语句的一种动态形式,
name 字符串即类名并会成为 __name__ 属性;
bases 元组包含基类并会成为 __bases__ 属性;如果为空则会添加所有类的终极基类 object。
dict 字典包含类主体的属性和方法定义;它在成为 __dict__ 属性之前可能会被拷贝或包装。
4. 下面两条语句会创建相同的 type 对象:
class X:
a = 1
X = type('X', (), dict(a=1))
5. 提供给三参数形式的关键字参数会被传递给适当的元类机制 (通常为 __init_subclass__()),相当于类定义中关键字 (除了 metaclass) 的行为方式。
6. 在 3.6 版更改: type 的子类如果未重载 type.__new__,将不再能使用一个参数的形式来获取对象的类型
- 传入一个参数时,返回 object 的类型。 返回值是一个 type 对象,通常与 object.class 所返回的对象相同
a = 1
a.__class__
type(a) # 跟上面的结果是一样的, 但不能1.__class__哦~
- isinstance
s1 = 'a'
isinstance(s1,str) # True
isinstance(s1,(str,int,list)) # 一样也是True
class A:
pass
class B(A):
pass
b = B()
isinstance(b,B) # True
isinstance(b,A) # 也是True
# 但是type不一样,不会检查继承
type(b) == B # True
type(b) == A # False
上面是最简单的使用,最关键的来了,type传入三个参数时,返回一个新的 type 对象。 这在本质上是 class 语句的一种动态形式
# 在Pycharm上查看type的定义 class type(object): """ type(object_or_name, bases, dict) # 2种用法 type(object) -> the object's type # 上面讲的用法1 type(name, bases, dict) -> a new type # 创建动态类的另外一种方式 """
1. name 字符串即类名并会成为 __name__ 属性;# 字符串类型 2. bases 元组包含基类并会成为 __bases__ 属性;如果为空则会添加所有类的终极基类 object。 3. dict 字典包含类主体的属性和方法定义;它在成为 __dict__ 属性之前可能会被拷贝或包装。# 这里可以填写类属性、类方式、静态方法,采用字典格式,key为属性名,value为属性值
动态创建类
官网案例
class X: a = 1 # 等价于 X = type('X', (), dict(a=1))
我们做点拓展
示例1:一些典型的错误
type('A',,) # SyntaxError: invalid syntax type('A',(),) # TypeError: type() takes 1 or 3 arguments type('A',(),1) # TypeError: type.__new__() argument 3 must be dict, not int
示例2:类的创建
# 依旧是错误的示范 type('A',(),{}) # 相当于创建了一个类的名字叫A a = A() # NameError: name 'A' is not defined 但你不能这么用 # 正确的 B= type('B',(),{}) b = B() type(b) # __main__.B
# 诡异的(不推荐) D = type('C',(),{}) x = C() # 哪个是对的? y = D() # 哪个是对的? D = type('C',(),{}) x = C() # 提示报错了,NameError: name 'C' is not defined y = D() # 对的 type(y) # 结果是啥? __main__.C 你想到了吗?当然也还是可以理解的 # type('C',(),{}) 创建一个名叫C的类 # 但D这个变量接收了上面的返回值,C是没有这个变量的(不在内存中)
示例3:带继承的类的创建
class A: pass B = type('B',('A'),{}) # 这个错比较明显 # TypeError: type.__new__() argument 2 must be tuple, not str
class A: pass B = type('B',('A',),{}) TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases # 其实错在你不能用'A',这是个str,实际上此处应该是一个类名
- 应该这样
class A: pass B = type('B',(A,),{}) b = B() # 实例化没问题 print(B.__mro__) # mro也是ok的 # (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
示例4:带类属性的创建
C = type('C',(),{'name':'wuxianfeng'}) print(C.name) # 输出 wuxianfeng
- 注意是类属性,并不是实例属性
示例5:带实例方法的创建
def say(self): self.name = 'wuxianfeng' print(f'calling method say, my name is {self.name}') Person = type('Person',(),{'say':say}) wxf = Person() wxf.say() # calling method say, my name is wuxianfeng
多一个例子(因为这是最常见的做法)
def run(self): return 'calling run' Foo = type('Foo', (object,), {'value':1, 'func':lambda self:'calling func', 'run':run}) foo = Foo() print(foo.value) # 输出 1 result = foo.func() # 接收返回值‘calling func’ print(result) # 输出 calling func print(foo.run()) # 输出 calling run
说在最后
class创建类的本质就是用type创建,所以可以说python中所有类都是type创建的,包括整数、字符串、函数以及用户自定义的类
当type()只有一个参数时,其作用就是返回变量或对象的类型
当type()有三个参数时,其作用就是创建类对象
通过type添加的属性是类属性,不是实例属性
type就是Python的内建元类