вложенные классы в Python - PullRequest
       34

вложенные классы в Python

6 голосов
/ 08 января 2012

Работа с классами (вложенными и т. Д.) Выглядит нелегко в Python , на удивление !Следующая проблема появилась недавно и заняла несколько часов (попробуйте, поиск ...) безуспешно.Я прочитал большинство ссылок, связанных с SO, но ни одна из них не указала на проблему, представленную здесь!

#------------------------------------
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'

Где проблема в коде? И В обоих случаях кажется, что когда B (A) инициализируется, A () не инициализируется.Какое решение для этой проблемы?Обратите внимание, что термин A.__init__(), вызываемый внутри B () __init__(), не работает!

Обновления:

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()

, что приводит к:

Curvature 0.5
Curvature 0

что я искал.

Ответы [ 2 ]

7 голосов
/ 08 января 2012

Код, выполняемый в методе, выполняется в локальной области действия этого метода. Если вы обращаетесь к объекту, который не находится в этой области, Python будет искать его в глобальной области / модульной области, НЕ в области действия класса или области действия любого включающего класса!

Это означает, что:

A.a = 'a_b'

внутри C.B.__init__ установит атрибут класса глобального A класса, а не C.A, как вы, вероятно, предполагали. Для этого вам нужно сделать следующее:

C.A.a = 'a_b'

Кроме того, Python не будет вызывать родительские методы, если вы переопределите их в подклассах. Ты должен сделать это сам.

Правила области видимости означают, что если вы хотите вызвать метод __init__ родительского класса внутри C.B.__init__, он должен выглядеть следующим образом:

C.A.__init__(self)

и НЕ так:

A.__init__(self)

Возможно, это то, что вы пробовали.

1 голос
/ 08 января 2012

Вложенные классы кажутся такими непитонными, даже если их рассматривать как фабрики.Но, чтобы ответить на ваш вопрос: просто нет c5.a (экземпляр CB).В init-методе CB вы добавляете к классу CA атрибут a, но не к CB!Класс A уже имеет атрибут a, если он создан!Но объект класса B (и даже класса) этого не делает!

Вы также должны помнить, что __init__ не является конструктором, как в C ++ или Java!«Реальный конструктор» в python будет __new__.__init__ просто инициализирует экземпляр класса!

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

Таким образом, в этом примере c является атрибутом класса, где i является атрибутом экземпляра.

Еще большее любопытство - ваша попытка добавить атрибут в базовый класс вмомент создания экземпляра класса ребенка.Вы не получаете «поздний» атрибут наследования таким образом.Вы просто добавляете в класс A дополнительный атрибут, который удивляет меня даже работой.Я полагаю, вы используете Python 3.x?

Причина такого поведения?Ну, я думаю, это связано с аккуратной особенностью питонов, которая в определениях питонов выполняется (AFAIK).

По той же причине:

def method(lst = []):

почтиплохая идеяпараметр deafult связывается в момент определения, и вы не будете генерировать новый объект списка каждый раз, когда вызываете метод, но повторно используете один и тот же объект списка.

...