2015-03-07 1 views
1

Общий пример, когда речь идет о классе атрибутов в питоне, заключается в следующем:Почему назначение атрибутов класса в python ведет себя как присвоение переменных instace?

Python 2.7.6 (default, Sep 9 2014, 15:04:36) 
>>> class B(object): 
... cv = [] 
... 
>>> b1 = B() 
>>> b2 = B() 
>>> b1.cv, b2.cv, B.cv 
([], [], []) 
>>> b1.cv.append(1) 
>>> b1.cv, b2.cv, B.cv 
([1], [1], [1]) 
>>> b2.cv.append(2) 
>>> b1.cv, b2.cv, B.cv 
([1, 2], [1, 2], [1, 2]) 
>>> B.cv.append(3) 
>>> b1.cv, b2.cv, B.cv 
([1, 2, 3], [1, 2, 3], [1, 2, 3]) 

Это показывает, что атрибут класса распределяется между классом и всеми экземплярами.

Но вот что происходит, когда мы переназначить значение атрибута класса, то есть без мутации исходного объекта, ограниченного атрибуту класса:

>>> class A(object): 
... cv = 0 
... 
>>> a1 = A() 
>>> a2 = A() 
>>> a1.cv, a2.cv, A.cv 
(0, 0, 0) 
>>> a1.cv = 1 
>>> a1.cv, a2.cv, A.cv 
(1, 0, 0) 
>>> a2.cv = 2 
>>> a1.cv, a2.cv, A.cv 
(1, 2, 0) 
>>> A.cv = 3 
>>> a1.cv, a2.cv, A.cv 
(1, 2, 3) 

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

Почему такое поведение выглядит следующим образом?

Я не могу понять, что это за логика может привести к тому, что это приводит к так «не имеющим отношения к делу» для «неизменяемых» (A) и «изменчивых» (B) случаев. Это заставляет меня думать о " нет никакого смысла использовать переменные класса», поскольку они могут быть склонны к ошибкам ...

Я надеюсь, что это я, кто не видит свет в этом туннеле ...

+0

Эффекты класса можно эффективно использовать, если вы не собираетесь использовать их через экземпляры. Например, мне нравится управлять группой объектов одного класса в атрибутах класса. Если вы когда-либо слышали о Pygame, я чаще всего использую эту технику. –

+0

Да, когда я вижу это, я подумал: «a1.cv !? a2.cv !? это статические переменные класса! и я нахожу это довольно обескураживающим, что Python позволяет смешивать и сопоставлять, как он используется. Я бы воздержался от постоянного доступа к классу static через члены экземпляра и никогда не знал, что есть проблема. Что касается того, почему Python позволяет вам выполнить приведенный выше код, я думаю, это объясняется в нижеследующем ответе, но кажется, что это хороший способ попасть в неприятности: p –

+0

@MalikBrahimi, было бы здорово, если бы вы предоставили какой-то крошечный пример такой техники, просто чтобы понять реальный случай, когда это можно использовать ... И поместите некоторую ссылку на gist или pastebin или что-то еще ... Спасибо! – yashaka

ответ

2

в первом примере, вы мутировать список. Существует только один экземпляр списка во вселенной, B.__dict__['cv']. Во втором примере вы присваиваете значения. Когда вы это делаете, они присваиваются в каждом конкретном экземпляре a(1|2|3), потому что это то, как настройка атрибута работает в Python (он сохраняет __dict__ того, что вы пытаетесь изменить атрибут). Вам необходимо будет изменить A.cv, чтобы изменить все, и любые изменения, сделанные в a(1|2|3), будут отменять сделанные изменения.

(Python пытается использовать a(1|2|3).__dict__, а затем возвращается к A.__dict__.)

1

Просто еще один последний пример, объясняющий ответ Крис Уоррик

>>> A.cv = 0 
>>> a1, a2 = A(), A() 
>>> A.cv, a1.cv, a2.cv 
(0, 0, 0) 
>>> A.cv = 1 
>>> A.cv, a1.cv, a2.cv 
(1, 1, 1) 
>>> a1.cv = 2 # Here the new instance attribute is created for a1, 
# and so it will hide the class attribute with the same name, 
# once getting the value from instance namespace 
>>> A.cv, a1.cv, a2.cv 
(1, 2, 1) 
>>> A.cv = 3 
>>> A.cv, a1.cv, a2.cv 
(3, 2, 3) 
0

Вы можете использовать атрибуты класса эффективно, если вы не» t намерены использовать их через экземпляры. Например, мне нравится управлять группой объектов одного класса в атрибутах класса. Если вы когда-либо слышали о Pygame, я чаще всего использую эту технику.

class Alien: 
    sprites = [] 

    def __init__(self, x, y): 
     self.surf = pygame.image.load('Alien.png') 
     self.rect = self.surf.get_rect() 

     self.rect.topleft = (x, y) 
     Alien.sprites.append(self) 

    @staticmethod 
    def draw_sprites(screen): 
     for sprite in Alien.sprites: 
      screen.blit(sprite.surf, sprite.rect) 

Вы видите, как управление объектами может быть таким простым с помощью методов и атрибутов класса?