【问题标题】:Give class instances different methods depending on instance variables根据实例变量给类实例不同的方法
【发布时间】:2014-03-06 18:39:12
【问题描述】:

我想创建一个类,其中实例根据其变量具有不同的方法。

例如:

class Spam:
    def __init__(self, eggs):
        self.eggs = eggs

那么,如果 Spamself.eggs 为 5,则它应该将 foo 定义如下:

def foo(self):
    print(self.eggs)

否则

def foo(self, excited):
    if excited:
       print("{}!!!!!".format(self.eggs))
    else:
       print("{}...".format(self.eggs))

目前,我通过在类外部声明两个foo 函数(具有不同名称)并在__init__ 中设置实例作为方法的方法来做到这一点。但是,这会从类定义中删除函数定义,我真的不想这样做。还有其他方法吗?

编辑:实际上下文。

我正在制作一些类来表示图中的节点,其中每个图都可以加权或不加权。 我希望节点有一个link 方法,将另一个节点作为参数,将它们连接在一起。加权图中的节点(即那些self.graph.weightedTrue 的节点)应该有一个link 方法和另一个指定连接边权重的参数。有没有更好的方法来做到这一点?例如,将权重参数设为可选,但存在于所有节点的 link 方法中。

【问题讨论】:

  • 基于状态切换 API 并不是一个好主意。
  • @MartijnPieters 我已编辑问题以提供实际上下文。
  • 投反对票有什么原因吗?
  • 听起来你想要一个基本的图类,并从中派生一个加权图类。或者,如果您认为不值得为加权图创建一个全新的类,只需为 link 方法添加一个 weight 参数,默认为 1

标签: python class


【解决方案1】:

扩展一下 jme 提到的内容:

默认参数方式:

class Node:
    def __init__(self, name):
        self.name = name
        self.links = []

    def link(self, other, weight = None):
        self.links.append((other, weight))

继承方式:

class Node:
    def __init__(self, name):
        self.name = name
        self.links = []

    def link(self, other):
        #one of the following, depending if other methods of this base class will be used in the derived class
        self.links.append((other, None))
        #self.links.append(other)

class WeightedNode(Node):
    def link(self, other, weight):
        self.links.append((other, weight))

【讨论】:

    【解决方案2】:

    你可以使foo只有Spam的一种方法,默认参数excited

    In [49]: class Spam:
        ...:    def __init__(self, eggs):
        ...:        self.eggs = eggs
        ...: 
        ...:    def foo(self, excited=False):
        ...:        if self.eggs == 5:
        ...:            print(self.eggs)
        ...:            return
        ...: 
        ...:        if excited:
        ...:           print("{}!!!!!".format(self.eggs))
        ...:        else:
        ...:           print("{}...".format(self.eggs))
    
    In [50]: Spam(5).foo()
    5
    
    In [51]: Spam(6).foo()
    6...
    
    In [52]: Spam(6).foo(True)
    6!!!!!
    

    【讨论】:

      【解决方案3】:

      这是您需要利用“工厂模式”的地方。也就是说,您需要有 N 个类(并非所有与继承相关,而是与鸭类型相关),这样:

       def create_my_class(arg1, arg2, arg3=None, arg4=None):
         #optional arguments? Ah ha!
         if arg3 is not None and arg4 is not None:
           return MyClass(arg1, arg2, arg3, arg4)
         elif arg3 is not None:
           return AnotherClass(arg1, arg3)
         else:
           return YetAnotherClass(arg1, arg2)
      

      所以你解耦了每个类的实现,而不是它的构造。然后,您可以使用(和替代)此方法来帮助创建不同的类。

         node1 = create_my_class(1, 1)
         node2 = create_my_class(1, 2, node1)
         node3 = create_my_class(1, 3, node1, 15)
         #etc...
      

      【讨论】:

      • 嗯,我总是对 python 中的工厂(Java 的灾难和厄运)有点怀疑。在您的具体情况下,为什么不在基类上实现__new__
      • @Hyperboreus 好吧,在Java 中,人们都为“企业”而疯狂。在 Python 中,我们有函数和鸭子类型。因此,在一个类上添加__new__ 会将该类绑定到我们想要通过调用创建的任何种类的类。使用这样的函数就不需要通过一个通用的 mixin 或类将不同的类强耦合在一起。
      • 非常真实和好的观点。我仍然看不到工厂模式与 OP 的问题有何关系。他需要将加权或未加权的节点链接在一起。我个人认为除了 jme 的评论(继承或默认 arg)之外的任何东西都是用大炮向麻雀射击。
      猜你喜欢
      • 2017-02-15
      • 1970-01-01
      • 1970-01-01
      • 2014-10-18
      • 1970-01-01
      • 1970-01-01
      • 2014-09-21
      • 2021-06-03
      • 1970-01-01
      相关资源
      最近更新 更多