Python 2中修饰子类中的super()

Python 2中修饰子类中的super(),python,decorator,python-2.x,Python,Decorator,Python 2.x,我试图在一个子类中使用super,该子类使用类装饰器包装在另一个类中: def class_decorator(cls): class WrapperClass(object): def make_instance(self): return cls() return WrapperClass class MyClass(object): def say(self, x): print(x) @class_dec

我试图在一个子类中使用
super
,该子类使用类装饰器包装在另一个类中:

def class_decorator(cls):
    class WrapperClass(object):
        def make_instance(self):
            return cls()
    return WrapperClass

class MyClass(object):
    def say(self, x):
        print(x)

@class_decorator
class MySubclass(MyClass):
    def say(self, x):
        super(MySubclass, self).say(x.upper())
def class_decorator(cls):
    class WrapperClass(object):
        def make_instance(self):
            i = cls()
            i._wrapped = cls
            return i
    return WrapperClass

class MyClass(object):
    def say(self, x):
        print(x)

@class_decorator
class MySubclass(MyClass):
    def say(self, x):
        super(self._wrapped, self).say(x.upper())

# make_instance returns inst of the original class, non-decorated i = MySubclass().make_instance() i.say('hello')
但是,调用
super
失败:

>>> MySubclass().make_instance().say('hello')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in say
TypeError: super(type, obj): obj must be an instance or subtype of type
这是可行的,但不是直观的,需要为每个修饰的子类重复。我正在寻找一种不需要为每个修饰过的子类添加额外样板的方法——在一个地方(比如,装饰器)添加更多代码就可以了

更新:在Python3中这不是问题,因为您可以使用
\uuuu class\uuuu
(或
super
变量,不带参数),因此以下功能可以正常工作:

@class_decorator
class MySubclass(MyClass):
    def say(self, x):
        super().say(x.upper())

不幸的是,对于这个项目,我一直使用Python 2.7。

问题是,您的装饰程序返回的类与Python(或使用您的代码的任何人)所期望的不同
super
不工作只是众多不幸后果之一:

>>> isinstance(MySubclass().make_instance(), MySubclass)
False
>>> issubclass(MySubclass, MyClass)
False
>>> pickle.dumps(MySubclass().make_instance())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.MySubclass'>: it's not the same object as __main__.MySubclass
现在
super
和其他一切都将按预期工作:

>>> MySubclass().make_instance().say('hello')
HELLO

出现此问题的原因是,在调用MySubclass.say()时,全局符号MySubclass不再引用代码中定义的“class MySubclass”。它是WrapperClass的一个实例,它与MySubclass没有任何关系

如果您使用的是Python3,您可以通过不向“super”传递任何参数来解决此问题,如下所示:

def class_decorator(wrapped_cls):
    @classmethod
    def make_instance(cls):
        return cls()

    wrapped_cls.make_instance = make_instance
    return wrapped_cls
super().say(x.upper())
我真的不知道为什么要使用您拥有的特定构造,但看起来确实很奇怪,MyClass的子类定义了“say()”,并且在源代码中自己有一个“say()”方法,最终将成为没有该方法的东西,这在您的代码中就是如此

注意:您可以更改
类包装器class
行以使其可读

class WrapperClass(cls):
这将使您的包装器成为您刚才装饰的包装器的子类。这对super(SubClass,self)调用没有帮助-您仍然需要删除args(这仅在Python3上是可以的),但至少创建为x=MySubclass()的实例会有一个“say”方法,正如人们第一眼看到的那样

编辑:我想出了一种方法来解决这个问题,但它看起来确实很奇怪,缺点是让“wrapped”类知道它正在被包装(并且它变得依赖于此,如果您删除装饰器,它将无法使用):


本质上,
\u wrapped
保存了声明时的类引用,这与使用常规的super(这个类的名称,self)内置调用是一致的。

Hmm.您是正确的,扩展现有类将是一个更好的选择,但我认为在我最初的用例中不可能这样做。为了说明这个问题,我发布的代码被简化了。在实际代码中,decorator返回一个描述符,该描述符封装了一个封装原始类的类。还是+1来说明我需要注意的其他陷阱。@FlorianBrucker好吧,那么问题是你的架构。用描述符替换一个类只会招惹麻烦,你不这么认为吗?考虑对代码的接口的影响——没有人可以访问修饰的类(因为它不再绑定到任何名称),有效地使其私有化。这就是你想要的吗?您很可能应该为描述符使用不同的名称。除了OP中的问题外,该体系结构一直在为预期目的完美工作。装饰器易于使用,并显著减少了样板文件。它的内部结构很复杂,但就其工作而言,这对我来说没关系;)正如我所写的,我没有以传统的方式使用这些类。它们并不打算被直接实例化,而是作为一种优雅的方式来使用,在某些情况下可以显著减少样板文件。您关于Python3的评论很中肯(请参阅我之前的更新),但不幸的是,对于这个项目,我一直使用Python2.7。
def class_decorator(cls):
    class WrapperClass(object):
        def make_instance(self):
            i = cls()
            i._wrapped = cls
            return i
    return WrapperClass

class MyClass(object):
    def say(self, x):
        print(x)

@class_decorator
class MySubclass(MyClass):
    def say(self, x):
        super(self._wrapped, self).say(x.upper())

# make_instance returns inst of the original class, non-decorated i = MySubclass().make_instance() i.say('hello')