用Python装饰类以更改父对象类型

3 投票
3 回答
2938 浏览
提问于 2025-04-15 14:15

假设你有两个类,分别叫做 X 和 Y。你想给这两个类添加一些属性,从而生成新的类 X1 和 Y1。

举个例子:

class X1(X):
  new_attribute = 'something'

class Y1(Y):
  new_attribute = 'something'

这里的 new_attribute 对于 X1 和 Y1 来说都是一样的。X 和 Y 之间没有什么特别的关系,唯一的限制是不能多重继承。还有一组其他的属性,但为了说明问题,这里就简单化了。

我觉得我可能把事情搞得太复杂了,但我原本想用一个装饰器,像这样:

def _xywrap(cls):
  class _xy(cls):
    new_attribute = 'something'
  return _xy

@_xywrap(X)
class X1():
   pass

@_xywrap(Y)
class Y1():
   pass

我感觉我可能错过了一个比较常见的模式,如果你能给我一些想法、意见和反馈,我会非常感激。

谢谢你的阅读。

布莱恩

编辑:示例:

这里有一段相关的摘录,可能会让事情更清楚。常见的类如下:

from google.appengine.ext import db

# I'm including PermittedUserProperty because it may have pertinent side-effects
# (albeit unlikely), which is documented here: [How can you limit access to a
# GAE instance to the current user][1].

class _AccessBase:
   users_permitted = PermittedUserProperty()
   owner = db.ReferenceProperty(User)

class AccessModel(db.Model, _AccessBase):
    pass

class AccessExpando(db.Expando, _AccessBase):
    pass

# the order of _AccessBase/db.* doesn't seem to resolve the issue
class AccessPolyModel(_AccessBase, polymodel.PolyModel):
    pass

这是一个子文档:

 class Thing(AccessExpando):
     it = db.StringProperty()

有时候,Thing 会有以下属性:

 Thing { it: ... }

而其他时候:

 Thing { it: ..., users_permitted:..., owner:... }

我一直搞不清楚为什么 Thing 有时候会有它的 _AccessParent 属性,而有时候又没有。

3 个回答

2

为什么不能使用 多重继承 呢?

class Origin:
  new_attribute = 'something'

class X:
  pass

class Y:
  pass

class X1(Origin, X):
  pass

class Y1(Origin, Y):
  pass
3

针对你在 voyager的回答 上的评论,我来回应一下:

from google.appengine.ext import db

class Mixin(object):
    """Mix in attributes shared by different types of models."""
    foo = 1
    bar = 2
    baz = 3

class Person(db.Model, Mixin):
    name = db.StringProperty()

class Dinosaur(db.polymodel.PolyModel, Mixin):
    height = db.IntegerProperty()

p = Person(name='Buck Armstrong, Dinosaur Hunter')
d = Dinosaur(height=5000)

print p.name, p.foo, p.bar, p.baz
print d.height, d.foo, d.bar, d.baz

运行这个代码会得到下面的结果:

Buck Armstrong, Dinosaur Hunter 1 2 3
5000 1 2 3

这不是你想要的结果吗?

5

使用三个参数的 type

def makeSomeNicelyDecoratedSubclass(someclass):
  return type('MyNiceName', (someclass,), {'new_attribute':'something'})

这确实是你猜的那样,是一种相当流行的写法。

编辑:一般情况下,如果某个类有自定义的元类,你可能需要提取并使用它(用一个参数的 type)来替代 type 本身,以保持它的特性(这可能适用于你的 Django 和 App Engine 模型):

def makeSomeNicelyDecoratedSubclass(someclass):
  mcl = type(someclass)
  return mcl('MyNiceName', (someclass,), {'new_attribute':'something'})

在简单的情况下,这种方法也能正常工作(因为在没有自定义元类的简单情况下,type(someclass) 是 type)。

撰写回答