...">

Python中的动态类定义

动态对象定义可以理解为运行时的定义。与在类的常见关键字定义中使用的静态定义不同class,动态定义使用内联类type



类型元类



类型类通常用于获取对象的类型。例如这样:



h = "hello"
type(h)
<class 'str'>


但是它还有其他用途。它可以初始化新类型。如您所知,Python中的所有内容都是一个对象。因此,所有定义都有类型,包括类和对象。例如:



class A:
    pass
type(A)
<class 'type'>


尚不清楚为什么将一个类分配给一个类类型type,而不是为其实例:



a = A()
type(a)
<class '__main__.A'>


该对象a被分配一个类别作为类型。这就是解释器将对象视为类的实例的方式。该类本身具有类类型,type因为它是从基类继承的object



A.__bases__
(<class 'object'>,)


类类型object



type(object)
<class 'type'>


object默认情况下,所有类继承该类



class A(object):
    pass


和...一样:



class A:
    pass


定义的类将基类作为类型继承。但是,这不能解释为什么基类object是class type type关键是,它type是一个元类。如您所知,所有类都继承自基类object,该基类是metaclass类型type因此,所有类也都具有这种类型,包括元类本身type



type(type)
<class 'type'>


这是Python中的“输入端点”。类型继承链在类上关闭typetype类是Python中所有类的基础。这很容易验证:



builtins = [list, dict, tuple]
for obj in builtins:
    type(obj)
<class 'type'>
<class 'type'>
<class 'type'>


类是一种抽象数据类型,其实例将类引用作为类型。



用类型类初始化新类型



检查类型时,type使用单个参数初始化该类



type(object) -> type


这样,它返回对象的类型。但是,该类使用三个参数实现了不同的初始化方法,该方法返回一个新类型:



type(name, bases, dict) -> new type


类型初始化参数



  • name

    一个字符串,它定义新类(类型)的名称。
  • bases

    基类(新类将继承的类)的元组。
  • dict

    具有未来类属性的字典。通常在键中包含字符串,在值中包含可调用类型。


动态类定义



我们初始化新类型的类,提供所有必需的参数并调用它:



MyClass = type("MyClass", (object, ), dict())
MyClass
<class '__main__.MyClass'>


您可以照常使用新类:



m = MyClass()
m
<__main__.MyClass object at 0x7f8b1d69acc0>


而且,该方法等效于通常的类定义:



class MyClass:
    pass


动态定义类属性



空类没有什么意义,所以出现了一个问题:如何添加属性和方法?



要回答此问题,请考虑初始初始化代码:



MyClass = type(“MyClass”, (object, ), dict())


通常,属性在初始化阶段作为第三个参数(字典)添加到类中。您可以在字典中指定属性名称和值。例如,它可以是一个变量:



MyClass = type(“MyClass”, (object, ), dict(foo=“bar”)
m = MyClass()
m.foo
'bar'


动态方法定义



可调用对象也可以传递给字典,例如,方法:



def foo(self):
    return “bar”
MyClass = type(“MyClass”, (object, ), dict(foo=foo))
m = MyClass()
m.foo
'bar'


这种方法有一个明显的缺点-需要静态定义该方法(我认为在元编程任务的上下文中,这可以视为一种缺点)。此外,self在类主体之外定义带有参数的方法看起来很奇怪。因此,让我们回到没有属性的类的动态初始化:



MyClass = type(“MyClass”, (object, ), dict())


初始化一个空类之后,可以动态地向其添加方法,即无需显式的静态定义:



code = compile('def foo(self): print(“bar”)', "<string>", "exec")


compile是将源代码编译为对象的内置函数。该代码可以通过函数exec()执行eval()



编译功能参数



  • source

    源代码,可以是模块的链接。
  • filename

    将对象编译到的文件名。
  • 模式

    如果指定"exec",函数将把源代码编译成模块。


工作的结果compile是一个类对象code



type(code)
<class 'code'>


该对象code需要转换为方法。由于方法是函数,因此我们首先将类code对象转换为类对象function为此,导入模块types



from types import FunctionType, MethodType


我将导入MethodType它,因为稍后需要它将功能转换为类方法。



function = FunctionType(code.co_consts[0], globals(), “foo”)


FunctionType初始化方法参数



  • code

    类对象codecode.co_consts[0]是对co_consts描述符的调用,该类描述符code是在对象代码中带有常量的元组。将对象想象code为具有单个函数的模块,我们试图将其添加为类方法。0是它的索引,因为它是模块中唯一的常数。
  • globals()

    全局变量字典。
  • name

    指定函数名称的可选参数。


结果是一个函数:



function
<function foo at 0x7fc79cb5ed90>
type(function)
<class 'function'>


接下来,您需要将此函数添加为类方法MyClass



MyClass.foo = MethodType(function, MyClass)


一个非常简单的表达式,将我们的函数分配给一个类方法MyClass



m = MyClass()
m.foo()
bar


警告



在99%的情况下,您可以使用静态类定义。但是,元编程的概念很好地揭示了Python的内部结构。尽管在我的实践中确实存在这种情况,但很可能您将很难找到此处描述的方法的应用。



您是否使用过动态对象?也许用其他语言?



链接






All Articles