【问题标题】:Python 3 metaclass defined functions not visible in the class derived from metaclassPython 3 元类定义的函数在从元类派生的类中不可见
【发布时间】:2017-06-01 20:59:47
【问题描述】:

我正在研究基于 python 3 的元类,因为我理解元类是 typedef

属于一类。这是一个小例子

class smallClass(type):

    def __init__(self, clsname, bases, clsdict):
        super(smallClass, self).__init__(clsname, bases, clsdict)
        self.a = 10
        self.b = 15
        print(self.addnos())

    def addnos(self):
        return self.a + self.b

    def __new__(self, clsname, bases, clsdict):
        return super(smallClass, self).__new__(self, clsname, bases, clsdict)

class smallClass2(object, metaclass=smallClass):
    la = smallClass.addnos

clstest = smallClass2()

clstest.addnos() ''' this does not work:  AttributeError: 'smallClass2' object has no attribute 'addnos'  '''

奇怪的是函数 addnos 在 smallClass2 中是不可见的,即使它基于包含 addnos 函数的元类 smallClass

有人可以帮助理解如何访问元类中定义的函数吗?或者为什么 clstest.addnos() 不起作用?

函数 addnos 可以从元类访问,但不能从派生类访问,为什么?

这里换个方法试试,有同样的问题

class smallClass(type):


    def __init__(self, clsname, bases, clsdict):
        super(smallClass, self).__init__(clsname, bases, clsdict)
        self.a = 10
        self.b = 15
        print(self.addnos())

    def addnos(self):
        return self.a + self.b

    def __new__(self, clsname, bases, clsdict):
        return super(smallClass, self).__new__(self, clsname, bases, clsdict)


class smallClass2(object, metaclass=smallClass):
    pass

clstest = smallClass2()
clstest.addnos()

【问题讨论】:

  • 我的问题是为什么 clstest.addnos() 不起作用?
  • 当您将smallClass.addnos 分配给la 时,您在smallClass2 的字典中显式地为该函数创建了一个附加名称。没有它,尝试访问clstest.addnos 会失败,因为在实例上执行属性查找时不会参考元类。
  • Jim,感谢您的快速回复 print(clstest.la()) 的工作是测量函数 addnos 仍然属于 smallClass 并且它不会传播到 smallClass2。我没想到会这样

标签: python-3.x metaclass


【解决方案1】:

我不明白你的意图是什么,以及你是否真的需要一个元类。但这是正在发生的事情:

Python 中的元类是类对象类。我认为通过(iPython)终端上的示例很容易将其可视化:

In [1]: class A:
   ...:    pass
   ...: 
   ...: a = A()
   ...: 

In [2]: type(a)
Out[2]: __main__.A

In [3]: type(A)
Out[3]: type

在这种情况下,type(a) 返回的“A”是对象“a”的类,而“type(A)”返回的“type”是对象“A”的类——诀窍是在 Python 中,甚至类也是对象:它们都是“类型”或“类型”子类型的实例。这种“类中的类”的作用就是所谓的“元类”。

当你从“type”继承时,你创建的类的实例将是类本身。并且有语言的语法支持,以类语句上的“命名参数”的形式,为类体选择元类。

综上所述,这就是 Python 中属性检索的工作方式(缩短版 - 我不会进入“描述符”或添加很多步骤的 setattr、getattr 和 getattribute 机制):

当您从对象请求属性时,Python 首先(*)查看该对象的 __dict__ 属性,如果它包含请求的属性,则该属性必须是映射。如果没有,它会在对象的类上查找属性(也在类__dict__ 上)。如果对象的类没有属性,Python 会在对象的超类 上查找它——但它不会进入类的类来查找属性。 (它遵循元类,因为它们最终出现在类 __mro__ 属性中)。

所以,如果我这样做:

class MyMeta(type):
   test = "value from metaclass"
   other_test = "Other metaclass value"

class A:
   test = "value from ancestor class"

class B(A, metaclass=MyMeta):
   pass

b = B()

对于实例“b”,Python 可以找到“A”中定义的“test”,但它永远不会自动找到“MyMeta”中定义的“test”,也不会看到“other_test”。这就是您的代码中发生的事情。

如果需要从元类中定义的实例值中查看它们,则所要做的就是单步执行该类 - 因此,如果 B 中的方法想要到达上面的“other_test”,这将工作:

class B(A, metaclass=MyMeta):
   def method(self):
        print(self.__class__.other_test)

(不是test,但仍会被A 中的祖先类中定义的test 遮蔽)

总而言之,我发现您实际上需要一个元类来进行任何尝试的可能性很小。它看起来更像是您想要简单类继承的行为。

(*) 始终牢记这是对属性查找算法的超级简化描述。属性的第一次查找实际上是在实例的类本身中查找“描述符”对象。

【讨论】:

  • 感谢 jsbueno,您的回答很有帮助,但我仍然无法执行 addnos 函数。如果我在 smallClass2 中添加 def method(self): print(self.__class__.addnos) self.__class__.addnos(),我可以看到 addnos 的对象是可见的。是否可以提供使函数执行的更改?
  • 无论如何,我认为您尝试做的事情没有任何意义。如果你认为你真的需要一个元类,使用元类__init__ 函数将addnos 注入到新类本身。 (这是您在元类__init__ 上获得的“self”参数 - 您已经将“a”和“b”注入到类中):self.addno = self.__class__.__dict__['adnos'] 在 metaclss 中__init__
  • jsbueno,当我运行上面的代码时,出现以下错误:B.method() TypeError: method() missing 1 required positional argument: 'self'。我尝试了多种不同的方法。再次感谢您的帮助。
  • 请退后一步,告诉我们你想做什么。我能想到的将普通方法放在元类中的唯一可能原因是完全从实例中隐藏它们。
  • 我正在开发一个用作库的 python 应用程序框架。当一个类被实例化以创建一个模块时,我想选择一些东西,比如在函数上应用哪些装饰器,需要根据顶级功能选择强制执行哪些接口 API,添加调试语句以跟踪代码执行等。我开始看dabeaz.com精彩介绍。对于这个特定问题,我正在查看此演示文稿中的第 71 张幻灯片。 dabeaz.com/py3meta/Py3Meta.pdf
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-04
  • 1970-01-01
  • 2021-03-24
  • 1970-01-01
  • 2015-12-30
  • 1970-01-01
相关资源
最近更新 更多