【问题标题】:Playing with object creation玩弄对象创建
【发布时间】:2011-10-31 21:18:26
【问题描述】:

我有两个类,方法为foo:

Foo = type('Foo', (object,), {'foo': lambda s: 'Foo method'})
Bar = type('Bar', (object,), {'foo': lambda s: 'Bar method'})

我还有一些其他类,我需要根据参数将其从上述类之一中子类化。

我的解决方案:

class Subject(object):
    def __new__(cls, key):
        base = (Foo if key else Bar)

        name = cls.__name__ + base.__name__
        dict_ = dict(cls.__dict__.items() + base.__dict__.items())
        bases = (base, cls)

        t = type(name, bases, dict_)
        return base.__new__(t)

    def bar(self):
        return 'Subject method'

测试:

print(Subject(True).foo(), Subject(True).bar())
print(Subject(False).foo(), Subject(False).bar())

输出:

('Foo method', 'Subject method')
('Bar method', 'Subject method')

这个解决方案足够安全吗?还是我需要更多了解?有没有更多的pythonic方式来做这种不规则的事情?

【问题讨论】:

  • 创建两个类SubjectFooSubjectBar,分别继承自FooBar,然后编写一个函数Subject检查参数并返回正确的实例班级。以意想不到的方式破坏的可能性要小得多。
  • @ChrisLutz 您在我的解决方案中可以看到哪些意想不到的方式?

标签: python oop inheritance factory subclassing


【解决方案1】:

我想大多数人会看到上面的代码并推荐使用组合而不是继承。 Subject 将定义一个 foo 方法,该方法根据布尔值分派到正确的类。

或者,您可以根据需要使用工厂函数来创建 Foo 或 Bar。

def subject(selector):
     'Factory function that chooses between Foo and Bar'
     return Foo() if selector else Bar()

如果需要,让 Foo 和 Bar 都从一个公共类继承,这样工厂函数总是返回一个公共类的子类的实例。

【讨论】:

  • 某种namedtuple函数?
  • 我没有看到与命名元组的关系。
【解决方案2】:

如果您避免使用元类(魔术),代码将更具可读性(因此更具 Python 风格)。采用 Chris 和 Raymond 建议的方法,即

使用组合:

class Subject(object):
    def __init__(self, key):
        self.foo = (Foo if key else Bar)().foo
    def bar(self):
        return 'Subject method'

或使用工厂函数:

def Subject(key):
    class Subject(Foo if key else Bar):
        def bar(self):
            return 'Subject method'
    return Subject()

【讨论】:

    【解决方案3】:

    这里是一些可能由您当前方法引起的意外行为的示例,即使Subject 覆盖了来自FooBar 的某些内容,FooBar 版本的方法将是叫:

    class Subject(object):
        # all of your current methods as they were above
        def foo(self):
            return 'Subject foo'
    
    >>> Subject(False).foo()
    'Bar method'
    

    即使您可以通过将base.__dict__.items() 放在创建dict_ 的行上的cls.__dict__.items() 之前来解决此问题,我还是建议您完全放弃这种方法,可能使用Chris Lutz 的评论或Raymond's answer

    如果您只对动态方法感兴趣,我会建议以下方法。在Subject 类中创建方法的私有版本,然后在Subject.__init__() 中分配您要使用的任何方法:

    class Subject(object):
        def __init__(self, key):
            self.foo = self._Foo_foo if key else self._Bar_foo
        def _Foo_foo(self):
            return 'Foo method'
        def _Bar_foo(self):
            return 'Bar method'
    
    >>> Subject(True).foo()
    'Foo method'
    >>> Subject(False).foo()
    'Bar method'
    

    【讨论】:

    • 我同意原始元类方法不是要走的路。工厂函数或动态调度是更传统和可预测的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-07
    • 2013-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多