【问题标题】:Extending a class in Python inside a decorator在装饰器中扩展 Python 中的类
【发布时间】:2019-05-17 04:57:16
【问题描述】:

我正在使用装饰器来扩展某些类并向它们添加一些功能,如下所示:

def useful_stuff(cls):
  class LocalClass(cls):
    def better_foo(self):
      print('better foo')
  return LocalClass

@useful_stuff
class MyClass:
  def foo(self):
    print('foo')

不幸的是,由于非全局 LocalClass

MyClass 不再是 pickleable
AttributeError: Can't pickle local object 'useful_stuff.<locals>.LocalClass'
  • 我需要腌制我的课程。你能推荐一个更好的设计吗?
  • 考虑到一个类可以有多个装饰器,通过让 MyClass 继承所有功能来切换到多重继承会是更好的选择吗?

【问题讨论】:

  • 我听说有一个包叫dill 可以腌制比pickle 更多的东西。
  • 我想我会为这个用例使用继承或元类
  • 使用元类会起作用吗?
  • 这里似乎没有任何理由使用装饰器。只需将LocalClass 声明为全局可见的基类,并让MyClass 直接从它继承,而不是装饰MyClass。解决用多个基类替换多个装饰器的问题需要更详细地了解装饰器做什么才能提供好的答案。
  • 多重继承的主要问题是数据属性冲突;如果您从混合中继承的只是一个方法,则任何冲突都将由 MRO 解决。您的装饰师只是以不同的方式创建相同的 MRO。

标签: python python-3.x pickle python-decorators


【解决方案1】:

您需要设置元数据,使子类看起来像原来的:

def deco(cls):
    class SubClass(cls):
        ...
    SubClass.__name__ = cls.__name__
    SubClass.__qualname__ = cls.__qualname__
    SubClass.__module__ = cls.__module__
    return SubClass

类通过使用它们的模块和 qualname 来记录在哪里找到类。您的类需要位于与原始类相同的位置(如果它没有被装饰),因此 pickle 需要看到相同的模块和 qualname。这类似于 funcutils.wraps 对修饰函数所做的。

但是,将新方法直接添加到原始类而不是创建子类可能会更简单且不易出错:

def better_foo(self):
    print('better_foo')

def useful_stuff(cls):
    cls.better_foo = better_foo
    return cls

【讨论】:

  • 我在示例中添加了一个类方法。但是还有其他事情,比如覆盖方法需要由 SubClass 完成,所以我选择了继承。
  • @rhino2rhonda 它让新的类表现得像可以腌制的旧类。
  • @rhino2rhonda:答案已扩展。
  • @EthanK unpickling 类会保留本地类的功能吗? pickle 库不需要访问本地类中的代码吗?
猜你喜欢
  • 2020-08-20
  • 1970-01-01
  • 2013-01-22
  • 1970-01-01
  • 2021-12-01
  • 2016-08-18
  • 2018-05-08
  • 2019-07-20
  • 1970-01-01
相关资源
最近更新 更多