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中的“输入端点”。类型继承链在类上关闭
type
。元type
类是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
类对象code
。code.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的内部结构。尽管在我的实践中确实存在这种情况,但很可能您将很难找到此处描述的方法的应用。
您是否使用过动态对象?也许用其他语言?