gpt4 book ai didi

python - SqlAlchemy 元类混淆

转载 作者:太空狗 更新时间:2023-10-29 17:23:54 25 4
gpt4 key购买 nike

我试图在SqlAlchemy的类构建过程中注入(inject)一些我自己的代码。试图理解代码,我对元类的实现有些困惑。以下是相关的片段:

SqlAlchemy 的默认“元类”:

class DeclarativeMeta(type):
def __init__(cls, classname, bases, dict_):
if '_decl_class_registry' in cls.__dict__:
return type.__init__(cls, classname, bases, dict_)
else:
_as_declarative(cls, classname, cls.__dict__)
return type.__init__(cls, classname, bases, dict_)

def __setattr__(cls, key, value):
_add_attribute(cls, key, value)
declarative_base是这样实现的:
def declarative_base(bind=None, metadata=None, mapper=None, cls=object,
name='Base', constructor=_declarative_constructor,
class_registry=None,
metaclass=DeclarativeMeta):
# some code which should not matter here
return metaclass(name, bases, class_dict)

它是这样使用的:
Base = declarative_base()

class SomeModel(Base):
pass

现在我已经像这样派生了我自己的元类:
class MyDeclarativeMeta(DeclarativeMeta):
def __init__(cls, classname, bases, dict_):
result = DeclarativeMeta.__init__(cls, classname, bases, dict_)
print result
# here I would add my custom code, which does not work
return result

并像这样使用它:
Base = declarative_base(metaclass=MyDeclarativeMeta)

好的,现在我的问题:
  • print result在我自己的课上总是打印 None .
  • 代码似乎仍然有效!?
  • 为什么元类使用 __init__而不是 __new__
  • declarative_base返回此类的一个实例。它不应该返回一个具有属性 __metaclass__ 的类吗?有 MyDeclarativeMeta作为值(value)?

  • 所以我想知道为什么代码有效。由于 SqlAlchemy 人员显然知道他们在做什么,我认为我完全走错了路。有人能解释一下这里发生了什么吗?

    最佳答案

    先说第一件事。 __init__需要 返回 None . Python docs说“可能不返回任何值”,但在 Python 中“结束”一个函数而不点击 return 语句相当于 return None .所以明确返回 None (作为文字或通过返回导致 None 的表达式的值)也没有害处。

    所以__init__ DeclarativeMeta的方法你引用的那句话对我来说有点奇怪,但它并没有做错任何事。这里又是我添加的一些评论:

    def __init__(cls, classname, bases, dict_):
    if '_decl_class_registry' in cls.__dict__:
    # return whatever type's (our superclass) __init__ returns
    # __init__ must return None, so this returns None, which is okay
    return type.__init__(cls, classname, bases, dict_)
    else:
    # call _as_declarative without caring about the return value
    _as_declarative(cls, classname, cls.__dict__)
    # then return whatever type's __init__ returns
    return type.__init__(cls, classname, bases, dict_)

    这可以更简洁明了地写为:
    def __init__(cls, classname, bases, dict_):
    if '_decl_class_registry' not in cls.__dict__:
    _as_declarative(cls, classname, cls.__dict__)
    type.__init__(cls, classname, bases, dict_)

    我不知道为什么 SqlAlchemy 开发人员觉得有必要返回任何 type.__init__返回(仅限于 None )。也许它是对 future 的证明,当 __init__可能会返回一些东西。也许这只是为了与其他方法保持一致,其中核心实现是通过遵循父类(super class)实现的;通常你会返回父类(super class)调用返回的任何东西,除非你想对它进行后处理。然而,它实际上并没有做任何事情。

    所以你的 print result打印 None只是表明一切都按预期工作。

    接下来,让我们仔细看看元类的实际含义。元类只是类的类。与任何类一样,您可以通过调用元类来创建元类(即类)的实例。类块语法并不是创建类的真正原因,它只是用于定义字典然后将其传递给元类调用以创建类对象的非常方便的语法糖。
    __metaclass__属性不是魔法,它真的只是一个巨大的黑客来传达信息“我希望这个类块创建这个元类的一个实例而不是一个 type 的实例”通过一个反向 channel ,因为有没有适当的 channel 将该信息传达给口译员。 1

    举个例子可能会更清楚。采用以下类块:
    class MyClass(Look, Ma, Multiple, Inheritance):
    __metaclass__ = MyMeta

    CLASS_CONST = 'some value'

    def __init__(self, x):
    self.x = x

    def some_method(self):
    return self.x - 76

    这是执行以下操作的大致语法糖:
    dict_ = {}

    dict_['__metaclass__'] = MyMeta
    dict_['CLASS_CONST'] = 'some value'

    def __init__(self, x):
    self.x = x
    dict_['__init__'] = __init__

    def some_method(self):
    return self.x - 76
    dict_['some_method'] = some_method

    metaclass = dict_.get('__metaclass__', type)
    bases = (Look, Ma, Multiple, Inheritance)
    classname = 'MyClass'

    MyClass = metaclass(classname, bases, dict_)

    因此,“具有属性 __metaclass__ 具有 [元类] 作为值的类” IS 元类的一个实例!它们是完全一样的。唯一的区别是,如果您直接创建类(通过调用元类)而不是使用类块和 __metaclass__属性,那么它不一定有 __metaclass__作为一个attribute.3

    metaclass 的调用最后与任何其他类调用完全一样。它将调用 metaclass.__new__(classname, bases, dict_)创建类对象,然后调用 __init__在生成的对象上初始化它。

    默认元类, type , 只在 __new__ 中做任何有趣的事情.我在示例中看到的元类的大多数用途实际上只是实现类装饰器的一种复杂方式;他们想在创建类时做一些处理,此后不关心。所以他们使用 __new__因为它允许它们在 type.__new__ 之前和之后执行.最终的结果是每个人都认为 __new__是你在元类中实现的。

    但实际上你可以拥有一个 __init__方法;它将在创建后的新类对象上调用。如果您需要为类添加一些属性,或者在某个注册表中记录类对象,这实际上是比 __new__ 更方便的地方(也是逻辑上正确的地方)。 .

    1 在 Python3 中,这是通过添加 metaclass 来解决的作为基类列表中的“关键字参数”,而不是作为类的属性。

    2 实际上,由于正在构造的类和所有基类之间需要元类兼容性,因此它稍微复杂一些,但这是核心思想。

    3 并不是说​​即使是具有元类的类(除了 type 之外)以通常的方式创建也不一定必须具有 __metaclass__作为一个属性;检查类的类的正确方法与检查其他任何事物的类的方法相同;使用 cls.__class__ ,或申请 type(cls) .

    关于python - SqlAlchemy 元类混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12664385/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com