这是Pythonic吗?动态分配类方法

时间:2017-03-14 22:32:19

标签: python coding-style

根据类的输入,需要在公共接口(此处称为method)下使用不同的方法。哪一个更像Pythonic?

尝试#1

class ExampleClass(object):
   def __init__(self, a=None, b=None):
       self.a = a
       self.b = b
       if a is not None:
           self.method = self._a_method
       else:
           self.method = self._b_method

   def method(self):
       raise NotImplementedError

   def _method_a(self):
       return "I am a method that relies on input: " + self.a

   def _method_b(self):
       return "I am a method that relies on input: " + self.b

或尝试#2:

class ExampleClass(object):
    def __init__(self, a=None, b=None):
        self.a = a
        self.b = b

    def method(self):
        if self.a is not None:
           return self._method_a(self)
        else:
           return self._method_b(self)

   def _method_a(self):
       return "I am a method that relies on input: " + self.a

   def _method_b(self):
       return "I am a method that relies on input: " + self.b

在这种情况下可能并不重要,但在我的情况下,我有多个method,意味着方法#2违反了DRY原则(每个都有相同的if,else逻辑)各种method s)。

另一方面,在_method_*中动态分配__init__函数是否尴尬,因为它在尝试#1中失败了?

1 个答案:

答案 0 :(得分:0)

我想说这两种方法都显示出糟糕的OOP设计,我相信应该避免,除非您的整个ExampleClass API(包括构造函数)已经向客户公开,并且应该保留以便向后兼容。如果不是这种情况,我认为应该使用Factory pattern的某些变体,可能类似http://www.drdobbs.com/jvm/creating-and-destroying-java-objects-par/208403883中描述的“静态工厂方法”。使用这种方法,您可以根据参数轻松创建不同子类型的对象。此外,您可以提供不同名称的不同工厂方法,以更好地反映已创建对象的含义,而不仅仅是分析参数。

如果您的API应该被保留并且性能不是非常重要(并且考虑到您使用Python,我认为它是理所当然的),我会投票给ExampleClass一个包装器,它将所有实际工作委托给通过工厂创建的内部委托。像这样:

class ExampleClass(object):
   def __init__(self, a=None, b=None):
       if a is not None:
           self._delegate = ExampleAImpl(a)
       else:
           self._delegate = ExampleBImpl(b)

   def method(self):
       return self._delegate.methodImpl()


class ExampleAImpl(object):
   def __init__(self, a):
       self.a = a

   def methodImpl(self):
       return "I am a method that relies on input: " + self.a

class ExampleBImpl(object):
   def __init__(self, b):
       self.b = b

   def methodImpl(self):
       return "I am a method that relies on input: " + self.b

注意:此方法不适用于ExampleClass的继承。此外,如果您仍需要将ab作为公共属性,则可以完成此操作,但会使代码更加复杂。