从具有自引用的实例内部访问类变量时会发生什么?

时间:2020-12-31 11:24:10

One of my colleagues wrote a code similar to this:

我的一位同事写了一个与此类似的代码:

class A(object):
    foo = "bar"


class B(A):
    def baz(self):
        print self.foo

and against my personal belief, this worked! I come from a mainly Java background, and this hurts my eyes... Personally, I would have written it like this:

并且违背我的个人信仰,这有效!我来自一个主要的Java背景,这伤害了我的眼睛......就个人而言,我会这样写:

class A(object):
    foo = "bar"


class B(A):
    def baz(self):
        print A.foo  # or dynamically determine the superclass.

I understand, that in Python variable names are often compared to "labels". But this still leaves a sour taste in my mouth. What are the implications of writing code like this? Is it really a bad idea? Can something go wrong?

据我所知,在Python中,变量名通常与“标签”进行比较。但这仍然留下了我口中的酸味。编写这样的代码有什么意义?这真是个坏主意吗?出了什么问题?

The only "bad" thing I can imagine is that deeper down the class hierarchy, an instance variable may shadow the class variable... so, you could says it's... okayish?

我可以想象的唯一“坏”的事情是,在类层次结构的深处,一个实例变量可能会影响类变量......所以,你可以说它是......好吗?

4 个解决方案

#1


5  

You can basically do both.

你基本上可以做到这两点。

If your class has just one parent, you can reference the variable directly with the name.

如果您的类只有一个父级,则可以直接使用名称引用该变量。

class A(object):
    foo = "bar"


class B(A):
    def baz(self):
        print self.foo  

What you have done make sense if you use multi

如果你使用multi,你所做的就有意义了

class A(object):
    foo = "bar"

class A2(object):
    foo = "bar 2"

class B(A, A2):
    def baz(self):
        #print foo # would throw NameError: global name 'foo' is not defined
        #print self.foo # prints "bar"
        print A.foo # prints "bar"
        print A2.foo # prints "bar 2"  

EDIT: If we ignore that fact that Java doesn't have multiple inheritance, I think it behaves in the similar way.

编辑:如果我们忽略Java没有多重继承的事实,我认为它的行为方式类似。

public class A {
    String foo = "bar";
}

public class B extends A {

    public void baz() {
        System.out.println(this.foo);
    }

}
public static void main(String[] args) {
    B b = new B();
    b.baz(); // prints "bar"
}

The only difference is that in Java it is accessible with this.foo, super.foo but also foo, while in Python you can just use self.foo or <superclass>.foo, but not foo

唯一的区别是在Java中可以使用this.foo,super.foo以及foo访问它,而在Python中你可以使用self.foo或 .foo,但不是foo

#2


3  

Well, first of all, Python is not Java. Some things are just different.

嗯,首先,Python不是Java。有些事情只是不同。

According to the semantics of Python, name resolution in classes has to work that way. Otherwise there'd be no way of calling methods from an instance, since methods are also class attributes.

根据Python的语义,类中的名称解析必须以这种方式工作。否则,无法从实例调用方法,因为方法也是类属性。

You're right that an instance variable elsewhere could shadow the class variable. But in my opinion, that's a feature, not a bug. If you really wanted to be sure you were always accessing the class variable, you could do self.__class__.foo, but I don't think there's any need.

你是对的,其他地方的实例变量可能会影响类变量。但在我看来,这是一个功能,而不是一个错误。如果你真的想确定你总是访问类变量,你可以做self.__class__.foo,但我认为没有任何需要。

#3


0  

The only "bad" thing I can imagine is that deeper down the class hierarchy, an instance variable may shadow the class variable

我可以想象的唯一“坏”的事情是,在类层次结构的更深处,实例变量可能会影响类变量

It's actually possible without any deep hierarchy at all.

它实际上可能没有任何深层次结构。

class TestClass(object):
    var = 'foo'
    def __init__(self):
        self.var = 'bar'

x = TestClass()
print x.var
>>> 'bar'
del x.var
print x.var
>>>> 'foo'

But it's okay in Python.

但它在Python中没问题。

#4


0  

This is kind of a complicated question. Your two code examples have slightly different behavior.

这是一个复杂的问题。您的两个代码示例的行为略有不同。

In your colleague's implementation, it is possible for an instance of B to have its own value of foo. Nothing in the above code would establish one, but you could easily do so:

在您的同事的实现中,B的实例可能具有其自己的foo值。上面的代码中没有任何内容可以建立一个,但您可以轻松地这样做:

example_B = B()
example_B.foo = 'foobar'

Then example_B.baz() would return 'foobar'.

然后example_B.baz()将返回'foobar'。

If implemented with your version, example_B.baz() would not check the instance's value. Instead, it would return 'bar', regardless of what had been set on example_B.

如果使用您的版本实现,example_B.baz()将不检查实例的值。相反,它会返回'bar',无论在example_B上设置了什么。

In practice, you should usually be able to tell when designing your class whether a name should refer to instance-specific information or to class-specific information. Only class-specific information should be declared at the class level.

在实践中,您通常应该能够在设计类时告诉您名称是应该引用特定于实例的信息还是引用特定于类的信息。只应在类级别声明特定于类的信息。

Some people use class-level declarations as a shortcut to ensure that a value is available on all instances of a class even if they don't set it, but I find that this usually leads to confusion as soon as they declare a mutable variable as a "default value":

有些人使用类级声明作为快捷方式来确保某个类的所有实例都有值,即使它们没有设置它,但我发现这通常会导致混淆,因为它们声明一个可变变量为一个“默认值”:

class BadExample(object):
    accidentally_shared_value = []

Instead, you should generally declare instance-specific values in __init__:

相反,您通常应该在__init__中声明特定于实例的值:

class BetterExample(object):
    def __init__(self):
        self.unshared_value = []

Accidentally sharing mutable values is the main situation that can go wrong when misunderstanding what's a class attribute versus what's an instance attribute.

意外地共享可变值是在误解什么是类属性与什么是实例属性时可能出错的主要情况。

#1


5  

You can basically do both.

你基本上可以做到这两点。

If your class has just one parent, you can reference the variable directly with the name.

如果您的类只有一个父级,则可以直接使用名称引用该变量。

class A(object):
    foo = "bar"


class B(A):
    def baz(self):
        print self.foo  

What you have done make sense if you use multi

如果你使用multi,你所做的就有意义了

class A(object):
    foo = "bar"

class A2(object):
    foo = "bar 2"

class B(A, A2):
    def baz(self):
        #print foo # would throw NameError: global name 'foo' is not defined
        #print self.foo # prints "bar"
        print A.foo # prints "bar"
        print A2.foo # prints "bar 2"  

EDIT: If we ignore that fact that Java doesn't have multiple inheritance, I think it behaves in the similar way.

编辑:如果我们忽略Java没有多重继承的事实,我认为它的行为方式类似。

public class A {
    String foo = "bar";
}

public class B extends A {

    public void baz() {
        System.out.println(this.foo);
    }

}
public static void main(String[] args) {
    B b = new B();
    b.baz(); // prints "bar"
}

The only difference is that in Java it is accessible with this.foo, super.foo but also foo, while in Python you can just use self.foo or <superclass>.foo, but not foo

唯一的区别是在Java中可以使用this.foo,super.foo以及foo访问它,而在Python中你可以使用self.foo或 .foo,但不是foo

#2


3  

Well, first of all, Python is not Java. Some things are just different.

嗯,首先,Python不是Java。有些事情只是不同。

According to the semantics of Python, name resolution in classes has to work that way. Otherwise there'd be no way of calling methods from an instance, since methods are also class attributes.

根据Python的语义,类中的名称解析必须以这种方式工作。否则,无法从实例调用方法,因为方法也是类属性。

You're right that an instance variable elsewhere could shadow the class variable. But in my opinion, that's a feature, not a bug. If you really wanted to be sure you were always accessing the class variable, you could do self.__class__.foo, but I don't think there's any need.

你是对的,其他地方的实例变量可能会影响类变量。但在我看来,这是一个功能,而不是一个错误。如果你真的想确定你总是访问类变量,你可以做self.__class__.foo,但我认为没有任何需要。

#3


0  

The only "bad" thing I can imagine is that deeper down the class hierarchy, an instance variable may shadow the class variable

我可以想象的唯一“坏”的事情是,在类层次结构的更深处,实例变量可能会影响类变量

It's actually possible without any deep hierarchy at all.

它实际上可能没有任何深层次结构。

class TestClass(object):
    var = 'foo'
    def __init__(self):
        self.var = 'bar'

x = TestClass()
print x.var
>>> 'bar'
del x.var
print x.var
>>>> 'foo'

But it's okay in Python.

但它在Python中没问题。

#4


0  

This is kind of a complicated question. Your two code examples have slightly different behavior.

这是一个复杂的问题。您的两个代码示例的行为略有不同。

In your colleague's implementation, it is possible for an instance of B to have its own value of foo. Nothing in the above code would establish one, but you could easily do so:

在您的同事的实现中,B的实例可能具有其自己的foo值。上面的代码中没有任何内容可以建立一个,但您可以轻松地这样做:

example_B = B()
example_B.foo = 'foobar'

Then example_B.baz() would return 'foobar'.

然后example_B.baz()将返回'foobar'。

If implemented with your version, example_B.baz() would not check the instance's value. Instead, it would return 'bar', regardless of what had been set on example_B.

如果使用您的版本实现,example_B.baz()将不检查实例的值。相反,它会返回'bar',无论在example_B上设置了什么。

In practice, you should usually be able to tell when designing your class whether a name should refer to instance-specific information or to class-specific information. Only class-specific information should be declared at the class level.

在实践中,您通常应该能够在设计类时告诉您名称是应该引用特定于实例的信息还是引用特定于类的信息。只应在类级别声明特定于类的信息。

Some people use class-level declarations as a shortcut to ensure that a value is available on all instances of a class even if they don't set it, but I find that this usually leads to confusion as soon as they declare a mutable variable as a "default value":

有些人使用类级声明作为快捷方式来确保某个类的所有实例都有值,即使它们没有设置它,但我发现这通常会导致混淆,因为它们声明一个可变变量为一个“默认值”:

class BadExample(object):
    accidentally_shared_value = []

Instead, you should generally declare instance-specific values in __init__:

相反,您通常应该在__init__中声明特定于实例的值:

class BetterExample(object):
    def __init__(self):
        self.unshared_value = []

Accidentally sharing mutable values is the main situation that can go wrong when misunderstanding what's a class attribute versus what's an instance attribute.

意外地共享可变值是在误解什么是类属性与什么是实例属性时可能出错的主要情况。