【问题标题】:Use default init values of other classes使用其他类的默认初始值
【发布时间】:2020-03-31 13:59:15
【问题描述】:

我有 2 个具有一些功能的类:

class A:
   def __init__(self, one=1, two=2):
      self.one = one
      self.two = two

   def do_smt(self):
      ...

class B:
   def __init__(self, value="test"):
      self.value = value

   def do_smt(self):
      ...

我有一个第三类必须使用这两个类。

class C:
   def __init__(self, one=1, two=2, value="test"):
      self.A = A(one, two)
      self.B = B(value)

   def do_smt(self):
      ...

现在我这样做:new_class = C()

但是如果class A or B 的默认值发生了变化,那我也需要在class C 中进行更改。有没有办法以知道哪些参数是默认参数的方式编写class C?它不需要处理任何参数,还需要处理其他类期望的参数。

【问题讨论】:

  • C 的接口定义为接受A 对象、B 对象不是更有意义吗?这解决了歧义。如果AB这两个类的参数名相似怎么办?
  • 你会一直使用AB的默认值吗?或者您有时会想要不同于默认值的值吗?
  • 后续问题:如果您的课程具有以下签名怎么办:class A: def __init__(self, a, /, b, *args, c, **kwargs): ...class B: def __init__(self, x, /, y, *args, z, **kwargs): ...。如果C 接受AB 对象:class C: def __init__(self, a: A, b: B): ...,这一切都没有问题。
  • 你能提供一些明确的限制吗?否则,这个问题可能会变得任意复杂。可以有重叠的参数吗?可以有*args**kwargs,或仅位置参数吗? C 可以成为另一个使用此方案的类的一部分吗? C 是否总是只包含两个类,或者可能更多?

标签: python python-3.x


【解决方案1】:

可以使用inspect.signature获取C类的每个“基”类的__init__方法的参数,并让C.__init__接受变量关键字参数,这样就可以遍历“基” " 类并传递给 __init__ 方法每个只是它需要什么以及给定的关键字参数有什么。使用itertools.islice 忽略第一个参数,始终为self

import inspect
from itertools import islice

class C:
    bases = A, B
    params = {}
    for cls in bases:
        params[cls] = inspect.signature(cls.__init__).parameters

    def __init__(self, **kwargs):
        for cls in self.bases:
            setattr(self, cls.__name__, cls(**{key: kwargs[key] for key in 
                islice(self.params[cls], 1, None) if key in kwargs}))

这样:

c = C(one=3,value='hi')
print(c.A.one)
print(c.A.two)
print(c.B.value)

输出:

3
2
hi

【讨论】:

    【解决方案2】:

    您可以使用一些标记值(此处为 None)并仅在提供有意义的参数时才传递参数:

    class C:
       def __init__(self, one=None, two=None, value=None):
          if one is two is None:
              self.A = A()
          else:
              self.A = A(one, two)
          if value is None:
              self.B = B()
          else:
              self.B = B(value)
    

    这样,AB 的默认值会自行处理。

    【讨论】:

    • 有没有更简单的方法,如果我结合所有不同的类,我希望有大约 10 个不同的默认值。
    【解决方案3】:

    一种解决方案是将默认值分解为常量:

    DEFAULT_ONE = 1
    DEFAULT_TWO = 2
    
    class A:
       def __init__(self, one=DEFAULT_ONE, two=DEFAULT_TWO):
          pass
    

    同样使用class C 中的常量。

    【讨论】:

      【解决方案4】:

      在调用类A和B之前,给变量定义init值

      尝试在 C 类 init 中的调用之前添加这些:

      self.initA_one = A.one
      self.initA_two = A.two
      self.initB_value = B.value
      

      然后继续

      self.A = A (.,.)
      self.B = B (.)
      

      编辑:

      这就是我的意思。

      class C():
         def __init__(self, one=-1, two=-2, value="detest"):
            self.initA_one = A().one
            self.initA_two = A().two
            self.initB = B().value
            self.A = A(one, two)
            self.B = B(value)
      
         def do_smt(self):
            print()
      
      new_class = C()
      
      print(f'default A.one is {new_class.initA_one}, new value A.one is {new_class.A.one}.')
      print(f'default A.two is {new_class.initA_two}, new value A.two is {new_class.A.two}.')
      print(f'default B.value is {new_class.initB}, new B.value is {new_class.B.value}')
      

      给予

      default A.one is 1, new value A.one is -1.
      default A.two is 2, new value A.two is -2.
      default B.value is test, new B.value is detest
      

      【讨论】:

      • 什么是self.A = A (.,.)?这不是有效的 Python 语法。
      • 我懒得写A(一,二)
      • 什么是self - 它是C 的实例吗? A.one 是什么 - 它是 A.__init__(one=...) 的默认值吗?为什么将这些存储到 self 而不是局部变量?
      【解决方案5】:

      我不确定这是否完全符合您的要求,但基本上您可以让 C 决定给 A、B 什么,让 A、B 决定使用什么,在 A 和 B 中使用 **kwds 方法参数。

      与示例类 C2 的区别之一是,如果 C 具有不同的默认值,它将覆盖 A、B。

      还有另一种选择,在 C3 下,您可以使用保护值(不使用 None 来允许将其作为默认值)仅传递给 C3 的参数。

      class A:
         def __init__(self, one=1, two=2, **kwds):
            self.one = one
            self.two = two
      
         def do_smt(self):
            pass
      
      class B:
         def __init__(self, value="test", **kwds):
            self.value = value
      
      
      class C:
         def __init__(self, one=1, two=2, value="test"):
            self.A = A(one, two)
            self.B = B(value)
      
      
      class C2:
          """ your default values override those of A, B"""
      
          def __init__(self, one=1, two=2, value="test"):
            locals_ = locals()
            locals_.pop("self")
      
            self.A = A(**locals_)
            self.B = B(**locals_)
      
      
      undefined = NotImplemented
      
      class C3:
          """ your default values dont affect A and Bs"""
      
          def __init__(self, one=undefined, two=undefined, value="test"):
      
      
            locals_ = {k:v for k,v in locals().items() if k != "self" and v is not undefined}
      
            self.A = A(**locals_)
            self.B = B(**locals_)
      
            #can still use it locally
            self.one = one if one is not undefined else 11
            self.two = two if two is not undefined else 22
      
      
      
      
      c= C()
      
      print("c.A.one:", c.A.one)
      print("c.B.value:", c.B.value)
      
      c2= C2()
      
      print("c2.A.one:", c2.A.one)
      print("c2.B.value:", c2.B.value)
      
      
      c3= C3()
      
      print("c3.A.one:", c3.A.one)
      print("c3.one:", c3.one)
      print("c3.B.value:", c3.B.value)
      

      输出:

      c.A.one: 1
      c.B.value: test
      c2.A.one: 1
      c2.B.value: test
      c3.A.one: 1
      c3.one: 11
      c3.B.value: test
      

      您甚至可以拥有一个使用 **kwds 本身的 C 变体,并将其传递给 A、B,以防他们发现其中的价值。

      class C4:
          """ your default values dont affect A and Bs 
              and you can pass in anything.  
             Neither two or value are known to C and that's OK"""
      
          def __init__(self, one=undefined, **kwds):
            locals_ = locals()
      
            locals_ = {k:v for k,v in locals().items() if k not in ("self","kwds") and v is not undefined}
      
            locals_.update(**kwds)
      
            self.A = A(**locals_)
            self.B = B(**locals_)
      
            #can still use it locally
            self.one = one if one is not undefined else 11
      
      
      c4= C4(value="somevalue")
      
      print("c4.A.one:", c4.A.one)
      print("c4.A.two:", c4.A.two)
      print("c4.one:", c4.one)
      print("c4.B.value:", c4.B.value)
      

      输出:

      c4.A.one: 1
      c4.A.two: 2
      c4.one: 11
      c4.B.value: somevalue
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-11-22
        • 2017-12-18
        • 1970-01-01
        • 1970-01-01
        • 2012-01-16
        • 1970-01-01
        • 2013-01-17
        相关资源
        最近更新 更多