【问题标题】:super() in Python 2.x without args没有参数的 Python 2.x 中的 super()
【发布时间】:2023-03-05 20:56:01
【问题描述】:

尝试将 super(B, self).method() 转换为简单的 bubble() 调用。 做到了see below

在这个例子中是否可以引用 B 类?

class A(object): pass

class B(A):
    def test(self):
        test2()

class C(B): pass

import inspect
def test2():
    frame = inspect.currentframe().f_back
    cls = frame.[?something here?]
    # cls here should == B (class)

c = C()
c.test()

基本上,CB 的孩子,BA 的孩子。然后我们创建C 类型的c。然后对c.test() 的调用实际上调用了B.test()(通过继承),它调用了test2()

test2()可以得到父框架frame;通过frame.f_code 对方法的代码引用; self 通过frame.f_locals['self'];但type(frame.f_locals['self'])C(当然),但不是B,其中定义了方法。

有什么方法可以得到B

【问题讨论】:

  • 如果我可以问...你需要这个做什么?因为我几乎不认为这样的事情可能是任何事情的最佳方法。
  • 我实际上是在尝试通过某种简单的bubble() 调用来简化用户的super(B, self).test() 操作。其实我已经找到办法了。
  • 你不能super(self.__class__, self).test()吗?
  • @WoLpH:不,因为第一个参数可能是“这个类”而不是self.__class__,它可能是“这个类”的子类。如果这样可行,为什么 Python 不自己这样做并消除第一个参数是多余的?
  • @Slava:Python 3 中的 AFAIK super 可以不带参数调用。虽然我猜你想要 Python 2.x。

标签: python


【解决方案1】:

从下面找到了一种更短的方法来执行super(B, self).test() -> bubble()

(适用于多重继承,不需要参数,正确地与子类一起表现)

解决方案是使用inspect.getmro(type(back_self))(其中back_self 是来自被调用者的self),然后将其迭代为clsmethod_name in cls.__dict__,并验证我们拥有的代码引用是此类中的引用(在find_class_by_code_object(self)嵌套函数中实现)。

bubble() 可以使用*args, **kwargs 轻松扩展。

import inspect
def bubble(*args, **kwargs):
    def find_class_by_code_object(back_self, method_name, code):
        for cls in inspect.getmro(type(back_self)):
            if method_name in cls.__dict__:
                method_fun = getattr(cls, method_name)
                if method_fun.im_func.func_code is code:
                    return cls

    frame = inspect.currentframe().f_back
    back_self = frame.f_locals['self']
    method_name = frame.f_code.co_name

    for _ in xrange(5):
        code = frame.f_code
        cls = find_class_by_code_object(back_self, method_name, code)
        if cls:
            super_ = super(cls, back_self)
            return getattr(super_, method_name)(*args, **kwargs)
        try:
            frame = frame.f_back
        except:
            return



class A(object):
    def test(self):
        print "A.test()"

class B(A):
    def test(self):
        # instead of "super(B, self).test()" we can do
        bubble()

class C(B):
    pass

c = C()
c.test() # works!

b = B()
b.test() # works!

如果有人有更好的想法,让我们听听。

已知错误:(感谢 doublep)如果 C.test = B.test --> “无限”递归。尽管子类实际上拥有一个方法似乎是不现实的,但它已经从父类的='ed 中获得。

已知的 bug2:(感谢 doublep)装饰方法不起作用(可能无法修复,因为装饰器返回一个闭包)... 修复了 for _ in xrange(5) 的装饰器问题: ... frame = frame.f_back - 最多可以处理 5 个装饰器,如果需要可以增加。 我喜欢 Python!

性能比 super() 调用差 5 倍,但我们谈论的是每秒 200K 调用与每秒 100 万调用,如果这不在您最紧密的循环中 - 没有理由担心。

【讨论】:

  • 我不确定它是否适用于装饰功能。另外,我希望性能在您的bubble() 用例中根本不重要。
  • 另一个(在实际使用中不太可能)错误:如果子类 def foo...,那么 bar = foobar() 中的 bubble() 将无法正常工作,因为 method_name 将不正确评估为“foo”。
  • bar = foo:如果我先执行B.test_alias = B.test,然后执行c.test_alias(),它会按预期调用A.test()。性能似乎还可以,bubble() 为 5000 ns(纳秒),而 super(...) 为 900ns。这是一个下降,但我不认为它是一个巨大的。
  • 啊,对。对于super() 模拟器,这正是所需要的,甚至与如果该方法直接调用父级的实现会发生的情况相同。我想是时候睡觉了,我犯了太多逻辑错误;)
【解决方案2】:

虽然这段代码不应该用于任何正常目的。为了回答这个问题,这里有一些工作;)

import inspect

def test2():
    funcname = inspect.stack()[1][3]
    frame = inspect.currentframe().f_back
    self = frame.f_locals['self']

    return contains(self.__class__, funcname)

def contains(class_, funcname):
    if funcname in class_.__dict__:
        return class_

    for class_ in class_.__bases__:
        class_ = contains(class_, funcname)
        if class_:
            return class_

【讨论】:

  • 嗯,这基本上就是我在下面所做的,但是你不认为使用 inspect.getmro() 比 bases 更好吗?此外,您没有测试该函数实际上是调用它的函数,C 类也可能具有 test 函数。我的回答会解决这个问题。
  • 确实,我只是找不到mro方法,所以我自己模拟了它。你的方法肯定更好。虽然我仍然认为两者都是错误的解决方案。
猜你喜欢
  • 2014-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-05
  • 2022-06-23
  • 1970-01-01
  • 2018-09-04
相关资源
最近更新 更多