python的基本元类:纯python类似物?

Posted

技术标签:

【中文标题】python的基本元类:纯python类似物?【英文标题】:python's base metaclass: a pure-python analogue? 【发布时间】:2014-04-28 02:59:10 【问题描述】:

我不太明白基本元类是如何工作的(又名type)。有谁知道其功能的纯 python 类似物?

python 文档通常对难以用英语完整描述的 C 级代码执行此操作(例如,请参阅 the explaination of __getattribute__),但不适用于 type

我知道如何开始。由于使用类型的子类定义type 的行为有点像说“类型的工作方式与类型的工作方式一样”,所以我定义了一个鸭子类型的元类。它有一些作用,但还不够。

class MetaClassDuck(object):
    @classmethod
    def __new__(self, mcs, name, bases, attrs):
        """Create a new class object."""
        newcls = super(MetaClassDuck, self).__new__(mcs)
        newcls.__dict__.update(attrs)
        newcls.__name__ = name
        newcls.__bases__ = bases
        return newcls

    def __call__(cls, *args, **kwargs):
        """Calling a class results in an object instance."""
        ###########################################################
        # Fill in the blank:
        # I don't see a way to implement this without type.__new__
        ###########################################################
        return newobj

class MyClass(object):
    __metaclass__ = MetaClassDuck

    one = 1
    _two = 2

    @property
    def two(self):
        return self._two

# This bit works fine.
assert type(MyClass) is MetaClassDuck
assert MyClass.one == 1
assert isinstance(MyClass.two, property)

myobj = MyClass()
# I crash here:
assert myobj.one == 1
assert myobj.two == 2


class MyClass2(MyClass):
    three = 3

assert type(MyClass2) is MetaClassDuck
assert MyClass2.one == 1
assert isinstance(MyClass2.two, property)
assert MyClass2.three == 3

myobj2 = MyClass2()
assert myobj2.one == 1
assert myobj2.two == 2
assert myobj2.three == 3

【问题讨论】:

你到底想要什么?如果您想了解type 的实际作用,最好的方法可能是阅读source code。如果你想在 Python 中实现自己的对象系统,尽可能少地使用内置特性,那么如果你对元类、特殊方法查找、描述符等进行了足够的阅读,这是可以实现的,但它不会与内置系统的互操作就像真正的类一样。如果你想让一些东西完全兼容像type(thing)这样的内置API... 我不确定我是否理解您的问题。你想知道how metaclasses work吗? 您将不得不大量使用type.__new__ 之类的东西,没有什么意义。 我试图通过在python中重新实现type来澄清它的操作,如上面的__getattribute__ example。不知道怎么说更清楚=/ @Two-BitAlchemist:你想看看这个bitbucket.org/pypy/pypy/src/tip/pypy/objspace/std/typeobject.py 【参考方案1】:

__new__ 负责创建新实例,而不是 __call____call__ 只是将实例创建工作传递给__new__,并返回__new__ 返回的内容,如果需要,调用__init__

回答这个type(双关语)问题的最佳方法是挖掘C 代码。只需下载源代码,解压它并使用vim Objects/typeobject.c 或任何你用来阅读和修改代码的东西。

如果您查看它,您会发现 type 元类的所有组件的 C 实现。 __new__大得离谱,FIY。

def __call__(cls, *args, *kwds): 看起来像:

实际的 C 代码

static PyObject *
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)

    PyObject *obj;

    if (type->tp_new == NULL) 
        PyErr_Format(PyExc_TypeError,
                     "cannot create '%.100s' instances",
                     type->tp_name);
        return NULL;
    

    obj = type->tp_new(type, args, kwds);
    if (obj != NULL) 
#        /* Ugly exception: when the call was type(something),
#           don`t call tp_init on the result. */
        if (type == &PyType_Type &&
            PyTuple_Check(args) && PyTuple_GET_SIZE(args) == 1 &&
            (kwds == NULL ||
             (PyDict_Check(kwds) && PyDict_Size(kwds) == 0)))
            return obj;
#        /* If the returned object is not an instance of type,
#           it won`t be initialized. */
        if (!PyType_IsSubtype(obj->ob_type, type))
            return obj;
        type = obj->ob_type;
        if (PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS) &&
            type->tp_init != NULL &&
            type->tp_init(obj, args, kwds) < 0) 
            Py_DECREF(obj);
            obj = NULL;
        
    
    return obj;

# 由我添加,用于帮助 *** 的语法高亮显示正确呈现 cmets

大致相等的 Python 实现

这只是我理解type.__call__ 所做的pythonic 解释。 这不是它的重新实现!

我可能忽略了一些方面,因为我对 PyC API 还很陌生,所以请随时纠正我。但我会按如下方式实现:

def __call__(cls, *args, **kwds):
    #We`ll be naming the class reference cls here, in the C code it's called type.
    try:
        obj = cls.__new__(cls, args, kwds)
    except AttributeError:      
        #The code first checks whether there is a __new__ method, we just catch the AttributeError 
        #exception.
        raise TypeError('cannot create  instances', cls.__name__)
    else:
        #The last if block checks that no errors occurred *inside* cls.__new__ 
        #(in the C code: type->tp_new)                        
        cls.__init__(obj, args, kwds)
        #The last if block checks whether any exception occurred while calling __init__ 
        #(return NULL or return -1 tells the calling function that an error/exception occurred,               
        #IDK the difference between the two.)
        return obj

最后的笔记

我会检查 __new__ 的实现(称为 type_new) 如果您想了解 Python 内部的工作原理,请尝试学习 C API,然后阅读 C 源代码。 我对 Python C 源代码非常陌生,所以我可能忽略了一些东西。请知道的人纠正我!

【讨论】:

【参考方案2】:

我不知道有任何 Python 类似物;但是,如果您想知道确切是什么类型,则需要深入了解c 源代码。

通常,它与任何元类一样:根据各种规范对生成的类进行调整。

例如:

--> huh = type('my_type', (), 'some_var':7)
--> huh
<class '__main__.my_type'>
--> h = huh()
--> huh.some_var
7

这里,type 正在创建一个名为 my_type 的新类和一个名为 some_var 的类属性,其初始值为 7

如果您想查看 stdlib 中元类的有用但有些复杂的示例,请查看 3.4 中的新 Enum data type

【讨论】:

以上是关于python的基本元类:纯python类似物?的主要内容,如果未能解决你的问题,请参考以下文章

理解python元类

Python进阶:一步步理解Python中的元类metaclass

我可以使用 python 元类来跟踪单独文件中的子类吗?

python中的元类作用?

谈谈Python中元类Metaclass:什么是元类

python 用于进一步子类化和构建的Python元类的基本示例。