Наследование подклассов Python - PullRequest
3 голосов
/ 17 февраля 2011

Я пытаюсь создать некоторые классы, которые наследуются от родительского класса, который содержит подклассы, которые наследуются от других родительских классов. Но когда я изменяю атрибуты в подклассах любого потомка, это изменение влияет на все дочерние классы. Я стараюсь не создавать экземпляры, так как использую эту функцию позже.

Код ниже сводит проблему к минимуму. Последняя строка показывает неожиданный результат.

class SubclsParent(object):
    a = "Hello"

class Parent(object):
    class Subcls(SubclsParent):
        pass

class Child1(Parent):
    pass

class Child2(Parent):
    pass

Child1.Subcls.a # Returns "Hello"
Child2.Subcls.a # Returns "Hello"
Child1.Subcls.a = "Goodbye"
Child1.Subcls.a # Returns "Goodbye"
Child2.Subcls.a # Returns "Goodbye" / Should still return "Hello"!

Ответы [ 5 ]

9 голосов
/ 17 февраля 2011

Поведение, которое вы видите, именно то, что вы должны ожидать.Когда вы определяете класс

>>> class Foo(object): pass
...

, вы можете изменить этот класс - не его экземпляры, а сам класс - потому что класс - это просто еще один объект, хранящийся в переменной Foo,Так, например, вы можете получить и установить атрибуты класса:

>>> Foo.a = 1
>>> Foo.a
1

Другими словами, ключевое слово class создает новый тип объекта и связывает указанное имя с этим объектом.


Теперь, если вы определите класс внутри другого класса (кстати, это странная вещь), что эквивалентно определению локальной переменной внутри тела класса.И вы знаете, что делает определение локальных переменных внутри тела класса: он устанавливает их как атрибуты класса.Другими словами, переменные, определенные локально, хранятся в объекте класса, а не в отдельных экземплярах.Таким образом,

>>> class Foo(object):
...     class Bar(object): pass
...

определяет класс Foo с одним атрибутом класса, Bar, который сам по себе является классом.Здесь не существует подклассов - классы Foo и Bar полностью независимы.(Достигнутое вами поведение можно повторить следующим образом:

>>> class Foo(object):
...     class Bar(object): pass
...
>>> class Foo(object): pass
...
>>> class Bar(object): pass
...
>>> Foo.Bar = Bar

.)

Так что вы всегда изменяете одну и ту же переменную !Конечно, вы измените значения, которые видите;вы изменили их сами!


Похоже, ваша проблема в том, что вы несколько запутались между атрибутами экземпляра и класса, которые совсем не одно и то же.

A Атрибут class - это переменная, которая определяется по всему классу.То есть любой экземпляр класса будет иметь одну и ту же переменную.Например, большинство методов являются атрибутами класса, так как вы хотите вызывать одни и те же методы для каждого экземпляра (обычно).Вы также можете использовать атрибуты класса для таких вещей, как глобальные счетчики (сколько раз вы создали экземпляр этого класса?) И другие свойства, которые должны совместно использоваться экземплярами.

Атрибут instance является переменнойсвойственный экземпляру класса.То есть каждый экземпляр имеет свою копию переменной с возможно различным содержанием.Здесь вы храните данные в классах - например, если у вас есть класс Page, вы хотите, чтобы атрибут contents хранился для каждого экземпляра, поскольку разные Page s, конечно, будут нуждаться в разных contents.

В вашем примере вы хотите, чтобы Child1.Subcls.a и Child2.Subcls.a были различными переменными.Естественно, они должны зависеть от экземпляра!


Это может быть немного прыжком, но вы пытаетесь реализовать интерфейсы в стиле Java в Python?Другими словами, пытаетесь ли вы указать, какие свойства и методы должен иметь класс , без фактического определения этих свойств?

Раньше это считалось чем-то непифоническим, что нужно делать, поскольку преобладающее согласие заключалось в том, что вы должны позволить классам делать все, что они хотят, и перехватывать исключения, возникающие, когда они не определяют необходимое свойство или метод.Тем не менее, недавно люди поняли, что интерфейсы на самом деле иногда полезны, и в Python были добавлены новые функциональные возможности, позволяющие это: абстрактные базовые классы .

3 голосов
/ 17 февраля 2011

Попробуйте это

class SubclsParent(object):
    def __init__(self):
         self.a = "Hello"

Когда вы определяете SubclsParent.a непосредственно в классе, вы определяете его как статический.

1 голос
/ 17 февраля 2011

Ваша проблема в том, что при доступе к атрибутам вы получаете доступ к унаследованным подпрограммам, которые были созданы в родительском классе, которые все ссылаются на одну и ту же переменную. Вы можете сделать эти переменные экземпляра, или вы можете создать атрибуты в дочерних классах, чтобы получить независимые атрибуты.

1 голос
/ 17 февраля 2011

Когда вы используете Child1.Subcls, python обнаруживает, что Child1.Subcls отсутствует, и поэтому проверяет Parent, где он его находит, и возвращает его.То же самое происходит с Child2.Subcls.В результате оба эти выражения относятся к одному и тому же классу.Child1 и Child2 не получают своих собственных подклассов, скорее, они имеют доступ к оригиналу.

* Я стараюсь не создавать экземпляры, так как позже я использую эту функцию.*

Я не понимаю, что вы здесь имеете в виду.

0 голосов
/ 17 февраля 2011

Возможно, вы действительно хотите метаклассы .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...