代码之家  ›  专栏  ›  技术社区  ›  int3

我应该如何在python中声明实例变量的默认值?

  •  69
  • int3  · 技术社区  · 15 年前

    我应该给我的类成员这样的默认值吗:

    class Foo:
        num = 1
    

    或者像这样?

    class Foo:
        def __init__(self):
            self.num = 1
    

    this question 我发现在这两种情况下,

    bar = Foo()
    bar.num += 1
    

    是一个定义明确的操作。

    我知道第一个方法会给我一个类变量,而第二个方法不会。但是,如果我不需要类变量,但只需要为实例变量设置一个默认值,那么这两种方法是否都一样好?或者其中一个比另一个更“蟒蛇”?

    我注意到的一件事是在Django教程中, they use the second method to declare Models. 我个人认为第二种方法更优雅,但我想知道“标准”方法是什么。

    5 回复  |  直到 7 年前
        1
  •  102
  •   Xavier Ho    15 年前

    扩展bp的答案,我想向您展示他所说的不变类型的含义。

    首先,这没关系:

    >>> class TestB():
    ...     def __init__(self, attr=1):
    ...         self.attr = attr
    ...     
    >>> a = TestB()
    >>> b = TestB()
    >>> a.attr = 2
    >>> a.attr
    2
    >>> b.attr
    1
    

    但是,这只适用于不可变(不可变)类型。如果默认值是可变的(意味着它可以被替换),则会发生以下情况:

    >>> class Test():
    ...     def __init__(self, attr=[]):
    ...         self.attr = attr
    ...     
    >>> a = Test()
    >>> b = Test()
    >>> a.attr.append(1)
    >>> a.attr
    [1]
    >>> b.attr
    [1]
    >>> 
    

    注意A和B都有一个共享属性。这通常是不需要的。

    当类型可变时,这是定义实例变量默认值的方法:

    >>> class TestC():
    ...     def __init__(self, attr=None):
    ...         if attr is None:
    ...             attr = []
    ...         self.attr = attr
    ...     
    >>> a = TestC()
    >>> b = TestC()
    >>> a.attr.append(1)
    >>> a.attr
    [1]
    >>> b.attr
    []
    

    我的第一个代码片段工作的原因是,对于不可变的类型,只要您需要,Python就会创建一个新的实例。如果您需要添加1到1,那么Python会为您生成一个新的2,因为旧的1不能更改。我相信原因主要是散列。

        2
  •  45
  •   miaout17    7 年前

    这两个片段做了不同的事情,所以这不是一个品味的问题,而是一个在你的上下文中什么是正确的行为的问题。 Python documentation 解释不同之处,但以下是一些示例:

    附件A

    class Foo:
      def __init__(self):
        self.num = 1
    

    这种束缚 num 到比赛场地 实例 . 对此字段的更改不会传播到其他实例。

    因此:

    >>> foo1 = Foo()
    >>> foo2 = Foo()
    >>> foo1.num = 2
    >>> foo2.num
    1
    

    附件B

    class Bar:
      num = 1
    

    这种束缚 号码 去酒吧 . 更改已传播!

    >>> bar1 = Bar()
    >>> bar2 = Bar()
    >>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation
    >>> bar2.num
    1
    >>> Bar.num = 3
    >>> bar2.num
    3
    >>> bar1.num
    2
    >>> bar1.__class__.num
    3
    

    实际答案

    如果我不需要一个类变量,但只需要为我的实例变量设置一个默认值,那么这两种方法是否都一样好?或者其中一个比另一个更“蟒蛇”?

    图表B中的代码显然是错误的:为什么要将类属性(实例创建时的默认值)绑定到单个实例?

    附件A中的代码是可以的。

    但是,如果要为构造函数中的实例变量提供默认值,我会这样做:

    class Foo:
      def __init__(num = None):
        self.num = num if num is not None else 1
    

    …甚至:

    class Foo:
      DEFAULT_NUM = 1
      def __init__(num = None):
        self.num = num if num is not None else DEFAULT_NUM
    

    …甚至:(首选,但如果且仅当您处理不可变类型时!)

    class Foo:
      def __init__(num = 1):
        self.num = num
    

    您可以这样做:

    foo1 = Foo(4)
    foo2 = Foo() #use default
    
        3
  •  4
  •   Duncan    15 年前

    使用类成员提供默认值非常有效,只要您只小心使用不可变的值。如果你试着用一张单子或口述来做,那将是非常致命的。它也适用于实例属性是对类的引用的情况,只要默认值为“无”。

    我已经看到这个技术在repoze中非常成功地使用,repoze是一个运行在zope之上的框架。这里的优点不仅是当类被持久化到数据库时,只需要保存非默认属性,而且当您需要在架构中添加一个新字段时,所有现有对象都会看到具有默认值的新字段,而不需要实际更改stor。ED数据。

    我发现它在更一般的编码中也能很好地工作,但它是一种风格。用你最喜欢的东西。

        4
  •  3
  •   Max Shawabkeh    15 年前

    使用类成员作为实例变量的默认值不是一个好主意,而且这是我第一次看到提到这个主意。它在您的示例中有效,但在许多情况下可能会失败。例如,如果值是可变的,在未修改的实例上对其进行修改将更改默认值:

    >>> class c:
    ...     l = []
    ... 
    >>> x = c()
    >>> y = c()
    >>> x.l
    []
    >>> y.l
    []
    >>> x.l.append(10)
    >>> y.l
    [10]
    >>> c.l
    [10]
    
        5
  •  0
  •   Realistic    10 年前

    还可以将类变量声明为“无”,这将阻止传播。当您需要一个定义良好的类并希望防止attributeErrors时,这非常有用。 例如:

    >>> class TestClass(object):
    ...     t = None
    ... 
    >>> test = TestClass()
    >>> test.t
    >>> test2 = TestClass()
    >>> test.t = 'test'
    >>> test.t
    'test'
    >>> test2.t
    >>>
    

    如果需要默认值:

    >>> class TestClassDefaults(object):
    ...    t = None
    ...    def __init__(self, t=None):
    ...       self.t = t
    ... 
    >>> test = TestClassDefaults()
    >>> test.t
    >>> test2 = TestClassDefaults([])
    >>> test2.t
    []
    >>> test.t
    >>>
    

    当然,仍然遵循其他答案中关于使用可变类型和不可变类型作为默认类型的信息。 __init__ .