简体   繁体   English

强制python子类init使用子类变量,而不是父类

[英]Force python subclass init to use subclass variables, not parent class

In the following code: 在以下代码中:

class A(object):
  VALUE = 1
  def __init__(self, value=VALUE):
    self.value = value

class B(A):
  VALUE = 2

i'd expect that B().value should be equal to 2, however: 我希望B()。值应该等于2,但是:

B().value = 1

Is there an elegant way to define a class hierarchy where child classes can just declare class variables they want to override and have them be defaults for the instance variables? 是否有一种优雅的方法来定义一个类层次结构,其中子类只能声明它们想要覆盖的类变量并使它们成为实例变量的默认值? I still want to allow for these to be changed on a per-instance level, eg. 我仍然希望允许在每个实例级别更改这些,例如。

b = B(value=3)

This is another default arguments question. 这是另一个默认参数问题。 The point is that when you write 关键是你写的时候

def foo(value=VALUE):

the code inside the function is compiled and made into a function object. 函数内部的代码被编译并成为一个函数对象。 It is at this time -- not at call time! 就在这个时候 - 而不是在通话时间! -- that the default arguments are stored. - 存储默认参数。 So by the time you have defined B it is too late: the default value of foo is already set and changing VALUE won't have any effect. 因此,当您定义B ,为时已晚: foo的默认值已经设置,更改VALUE将不会产生任何影响。

If this seems a strange thing to do, suppose foo was a global function: 如果这看起来很奇怪,假设foo是一个全局函数:

default = 3
def foo(x=default): pass

Then any other code, anywhere, could screw up foo by doing 然后任何其他代码,任何地方,可以搞砸了foo

global default
default = 4

This is arguably just as confusing. 这可以说是令人困惑的。


To force the lookups to be done at runtime not compile time, you need to put them inside the function: 要强制在运行时完成查找而不是编译时间,您需要将它们放在函数中:

def foo(value=None):
    self.value = self.VALUE if value is None else value

or (not quite the same but prettier) 或(不完全相同但更漂亮)

self.value = value or self.VALUE

(This is different because it will treat any 'falsy' value as a sentinel -- that is, 0 , [] , {} etc will all be overwritten by VALUE .) (这是不同的,因为它将任何 'falsy'值视为哨兵 - 即0[]{}等都将被VALUE覆盖。)


EDIT: @mgilson pointed out another way of doing this: 编辑:@mgilson指出了另一种方法:

def foo(**kwargs):
    self.value = kwargs.get("value", self.VALUE)

This is neater in that it doesn't require you to make a sentinel value (like None or object() , but it does change the argument specification of foo quite fundamentally since now it will accept arbitrary keyword arguments. Your call. 这更简洁,因为它不需要你创建一个sentinel值(如Noneobject() ,但它确实从根本上改变了foo的参数规范,因为现在它将接受任意关键字参数。你的调用。

The default value should not be define in the func declaration line, otherwise, when the python reads this line, the default value will be focused, and whatever you change the VALUE later, the default value will not be changed. 不应在func声明行中定义默认值,否则,当python读取此行时,默认值将被聚焦,无论您稍后更改VALUE,默认值都不会更改。

You could write it as follows: 您可以按如下方式编写它:

class A:
    VALUE = 1
    def __init__(self, value=None):
        if value is None:
            value = self.VALUE
        self.value = value

class B(A):
    VALUE = 2

print(A().value)
print(B().value)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM