1. 程式人生 > >來自stackoverflow的一個關於 python 巢狀類的問題(nested class)

來自stackoverflow的一個關於 python 巢狀類的問題(nested class)

http://stackoverflow.com/questions/8775246/nested-classes-in-python

有人在stack overflow上提問關於nested class in Python 的問題:

#------------------------------------
class A:
    def __init__(self):
        self.a = 'a'
        print self.a

class B(A):
    def __init__(self):
        self.b = 'b'
        A.a = 'a_b'
        print self.b, A.a
#------------------------------------
class C:
    class A:
        def __init__(self):
            self.a = 'a'
            print self.a

    class B(A):
        def __init__(self):
            self.b = 'b'
            A.a = 'a_b'
            print self.b, A.a
#------------------------------------
#------------------------------------
>>> c1 = A()
a
>>> c1.a
'a'
>>> c2 = B()
b 
>>> c2.a, c2.b
('a_b', 'b')
>>> c3 = C()
>>> c4 = c3.A()
a
>>> c4.a
'a'
>>> c5 = c3.B()
b a_b
>>> c5.b
'b'
>>> c5.a
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: B instance has no attribute 'a'
Where is the problem in the code? AND In both cases it seems that when B(A) is initialized A() is not initialized. What is the solution for this issue? Note that the term A.__init__() being called inside B()'s __init__()does not work!

前面的c2.a, c2.b 都可以好好的工作,怎麼c5.b就掛了呢?

下面是2個有用的回答:

answer 1:

The code executed in a method runs in the local scope of that method. If you access an object that is not in this scope, Python will look it up in the global/module scope, NOT

 in the class scope or the scope of any enclosing class!

This means that:

A.a ='a_b'

inside C.B.__init__ will set the class attribute of the global A class, not C.A as you probably intended. For that you would have to do this:

C.A.a ='a_b'

Also, Python will not call parent methods if you override them in subclasses. You have to do it yourself.

The scoping rules mean that if you wanted to call the __init__ method of the parent class inside C.B.__init__, it has to look like this:

C.A.__init__(self)

and NOT like this:

A.__init__(self)

which is probably what you've tried.

一個方法中的程式碼執行時如果發現某個物件在這個方法體中沒有,那麼它會去全域性/模組範圍裡去找,而不是去它的類或巢狀類。 看到這個答案我突然想到了另一個相似的問題,應該是相似的道理:
x = 1
def a():
    print x
    x = 2
    print x


answer 2:

Nested classes seems so unpythonic, even if considered as factories. But to answer your question: There simply is no c5.a (instance of C.B). In the init-method of C.B you add to the CLASS C.A an attribute a, but not to C.B! The class A does already have an attribute a, if instantiated! But the object of class B (and even the class) doesn't!

You must also keep in mind, that __init__ is not an constructor like in C++ or Java! The "real constructor" in python would be __new____init__ just initializes the instance of a class!

class A:
    c ='class-attribute'def __init__(self):
        self.i ='instance-attribute'

So in this example c is a class-attribute, where i is an attribute of the instance.

Even more curios, is your attempt to add an attribute to the baseclass at the moment of the instantiation of the child-class. You are not getting a "late" inheritance-attribute that way. You simply add to the class A an additional attribute, which surprises me to even work. I guess you are using python 3.x?

The reason for this behaviour? Well, i guess it has to do with pythons neat feature that in python definitions areexecuted(AFAIK).

The same reason why:

def method(lst =[]):

is almost ever a bad idea. the deafult-parameter gets bound at the moment of the definition and you won't generate a new list-object every-time you call the method, but reusing the same list-object.

這裡給出瞭如何解決問題的答案,就是使用顯示的呼叫,比如:C.A.a。另外也說了程式碼中存在的問題,就是類B中呼叫A.a其實不是作者的本意,作者用了未繫結方法。

answer 2最後還提到了不要用可變型別做函式可選引數了。

最後作者Update了正確方法:

class Geometry:
    class Curve:
        def __init__(self,c=1):
            self.c = c                          #curvature parameter
            print 'Curvature %g'%self.c
            pass                                #some codes

    class Line(Curve):
        def __init__(self):
            Geometry.Curve.__init__(self,0)     #the key point
            pass                                #some codes

g = Geometry()
C = g.Curve(0.5)
L = g.Line()