【问题标题】:Is this Pythonic? Assigning class methods dynamically [closed]这是Pythonic吗?动态分配类方法[关闭]
【发布时间】:2017-03-14 22:32:19
【问题描述】:

根据类的输入,需要在一个通用接口下使用不同的方法(这里称为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 原则(如果,否则每个逻辑中的逻辑都是相同的各种methods)。

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

【问题讨论】:

  • 如果这是工作代码,请将其发布到Code Review。在 Stack Overflow 上,您的问题过于基于意见(除非 Guido 碰巧回答)
  • 这两个选项都运行得很好并且做同样的事情。我想知道是否有“一种正确的方式”(或首选方式)来做到这一点。
  • 我肯定更喜欢第二种选择。
  • 如果方法 a 和 b 如此不同以至于这是必要的,那么假装它们是相同的方法是否有意义?您实际上不应该有两个不同的(子)类吗?这么一个微不足道的例子,我们怎么判断呢?
  • 我知道您想要一个一致的接口,但这并不一定意味着要编写一个类。此外,如果用户同时提供 a 和 b,或者两者都不提供怎么办?没有更多信息,这只能用意见来回答,实际上是xyproblem.info

标签: python coding-style


【解决方案1】:

我会说这两种方法都显示出糟糕的 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 仍然是公共属性,则可以这样做,但会使代码更加复杂。

【讨论】:

  • 谢谢,这是一个不错的方法。你不是说raise 中的def method(self): 是吗? (返回?)
  • @bill_e,是的,这是复制粘贴。固定为return
猜你喜欢
  • 2010-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-31
  • 1970-01-01
  • 2013-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多