базовое наследование (в питоне) - PullRequest
4 голосов
/ 24 февраля 2011

пользователей,

У меня есть базовый вопрос, касающийся наследования (в python). У меня есть два класса, и один из них унаследован от другого, как

class   p:
    def __init__(self,name):
        self.pname  = name

class   c(p):
    def __init__(self,name):
        self.cname  = name

Есть ли вероятность, что я смогу создать родительский объект и несколько дочерних объектов, которые ссылаются на ОДИН И ТО ЖЕ родительский объект? Это должно работать так, что родительский объект содержит несколько переменных, и всякий раз, когда я обращаюсь к соответствующим переменным от дочернего элемента, я фактически получаю доступ к переменной из родительского объекта. То есть если я изменяю его для одного потомка, он изменяется также для всех остальных детей, и данные сохраняются в памяти только один раз (и не копируются для каждого потомка ...)

Заранее спасибо.

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

class P:
    def __init__(self, name):
        self.pname = name

class C:
    def __init__(self, name,pobject):
        self.pobject = pobject
        self.cname = name

Это действительно состояние дел или существуют другие концепции?

Себастьян

Спасибо всем за помощь, в том числе и с именными соглашениями :) Но я все еще не очень доволен. Может быть, я приведу более сложный пример, чтобы подчеркнуть, что я действительно хочу сделать.

class P:
    data = "shareddata"
    def __init__(self,newdata):
        self.data = newdata 

    def printname(self):
        print self.name 

class C(P):
    def __init__(self,name):
        self.name = name

Теперь я могу сделать следующее

In [33]: c1 = test.C("name1")

In [34]: c2 = test.C("name2")

In [35]: c1.printname()
name1

In [36]: c2.printname()
name2

In [37]: c1.data
Out[37]: 'shareddata'

In [38]: c2.data
Out[38]: 'shareddata'

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

In [39]: c1.data = "tst"

In [40]: c2.data
Out[40]: 'shareddata'

In [41]: c1.data
Out[41]: 'tst'

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

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

ОБНОВЛЕНИЕ:

примечание к комментарию @eyquem: Спасибо за это, оно идет в нужном мне направлении. Однако теперь __class__.pvar является общим для всех объектов класса. Что я хочу, так это чтобы несколько экземпляров P имели разные pvar. Предположим, что P1 имеет pvar = 1, а P2 имеет pvar = 2. Затем я хочу создать дочерние элементы C1a, C1b, C1c, которые связаны с P1, то есть, если я скажу C1a.pvar, он должен получить доступ к pvar из P1. Затем я создаю C2a, C2b, C2c, и если я получаю доступ, то есть C2b.pvar, я хочу получить доступ к pvar из P2. Поскольку класс C наследует pvar от класса P, pvar известен C. Моя наивная идея заключается в том, что, если я создаю новый экземпляр CI, он сможет указать, какой (существующий) объект P следует использовать в качестве родительского объекта, а не создать совершенно новый объект P, как это делается при вызове P.__init__ внутри __init__ из C ... Для меня это звучит просто, может быть, я что-то забыл ...

UPDATE:

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

Есть предложения?

UPDATE:

Метод. class ._ подклассы _, похоже, больше не существуют ..

UPDATE:

Вот еще одна ссылка:

ссылка на обсуждение

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

UPDATE:

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

class P(object):
    def __init__(self,pvar):
        self.pobject    = None
        self._pvar  = pvar

    @property
    def pvar(self):
    if self.pobject != None:
        return  self.pobject.pvar
    else:
            return self._pvar
    @pvar.setter
    def pvar(self,val):
    if self.pobject != None:
        self.pobject.pvar = val
    else:
            self._pvar=val

    def printname(self):
    print self.name


class C(P):
    def __init__(self,name,pobject):  #<-- The same default `P()` is 
                                          # used for all instances of `C`, 
                                          # unless pobject is explicitly defined.
    P.__init__(self,None)
        self.name   = name
        self.pobject = pobject


p1  = P("1")
p2  = P("2")


c1a = C("c1a",p1)
c1b = C("c1b",p1)
c1c = C("c1c",p1)
c2a = C("c2a",p2)
c2b = C("c2b",p2)
c2c = C("c2c",p2)


print   id(c1a.pvar)
print   id(c1b.pvar)
print   id(c1c.pvar)
print   id(c2a.pvar)
print   id(c2b.pvar)
print   id(c2c.pvar)
print   id(p1.pvar)
print   id(p2.pvar)

print   id(c1a.name)
print   id(c1b.name)
print   id(c1c.name)
print   id(c2a.name)
print   id(c2b.name)
print   id(c2c.name)

Это немного громоздко, и я надеюсь, что есть более простой способ добиться этого. Но у него есть особенность, заключающаяся в том, что pvar упоминается только в классе P, а класс C не знает о pvar, как это должно быть в соответствии с моим пониманием наследования. Тем не менее, когда я создаю новый экземпляр C, я могу указать существующий экземпляр P, который будет храниться в переменной pobject. При обращении к переменной pvar происходит доступ к pvar экземпляра P, хранящегося в этой переменной ...

Выходные данные даются

3078326816
3078326816
3078326816
3074996544
3074996544
3074996544
3078326816
3074996544
156582944
156583040
156583200
156583232
156583296
156583360

Сейчас я прочитаю ваши последние комментарии,

всего наилучшего, Себастьян

ОБНОВЛЕНИЕ:

Я думаю, что самый элегантный способ был бы следующим (который НЕ РАБОТАЕТ)

class P(object):
    def __init__(self,pvar):
        self.pvar   = pvar

    def printname(self):
        print self.name


class C(P):
    def __init__(self,name,pobject):  
        P = pobject
        self.name   = name

Я думаю, что python должен допускать это ...

ОБНОВЛЕНИЕ:

Хорошо, теперь я нашел способ добиться этого, благодаря объяснениям eyquem.Но поскольку это действительно хак, официальная версия для той же версии должна быть ...

def replaceinstance(parent,child):
    for item in parent.__dict__.items():
        child.__dict__.__setitem__(item[0],item[1])
        print item

class P(object):
    def __init__(self,pvar):
        self.pvar   = pvar

    def printname(self):
    print self.name


class C(P):
    def __init__(self,name,pobject):
    P.__init__(self,None)
    replaceinstance(pobject,self)
        self.name   = name



p1  = P("1")
p2  = P("2")


c1a = C("c1a",p1)
c1b = C("c1b",p1)
c1c = C("c1c",p1)
c2a = C("c2a",p2)
c2b = C("c2b",p2)
c2c = C("c2c",p2)


print   id(c1a.pvar)
print   id(c1b.pvar)
print   id(c1c.pvar)
print   id(c2a.pvar)
print   id(c2b.pvar)
print   id(c2c.pvar)
print   id(p1.pvar)
print   id(p2.pvar)

print   id(c1a.name)
print   id(c1b.name)
print   id(c1c.name)
print   id(c2a.name)
print   id(c2b.name)
print   id(c2c.name)

вывод такой же, как и выше

3077745184
3077745184
3077745184
3074414912
3074414912
3074414912
3077745184
3074414912
144028416
144028448
144028480
144028512
144028544
144028576

ОБНОВЛЕНИЕ: Даже если идентификаторкажется правильным, последний код не работает, как ясно из этого теста

c1a.pvar    = "newpvar1"

print   c1a.pvar
print   c1b.pvar
print   c1c.pvar
print   c2a.pvar
print   c2b.pvar
print   c2c.pvar
print   p1.pvar
print   p2.pvar

он имеет вывод

newpvar1
1
1
2
2
2
1
2

Однако версия, которую я выложил первой, работает:

class P(object):
    def __init__(self,pvar):
        self.pobject    = None
        self._pvar  = pvar

    @property
    def pvar(self):
    if self.pobject != None:
        return  self.pobject.pvar
    else:
            return self._pvar
    @pvar.setter
    def pvar(self,val):
    if self.pobject != None:
        self.pobject.pvar = val
    else:
            self._pvar=val

    def printname(self):
    print self.name


class C(P):
    def __init__(self,name,pobject):  #<-- The same default `P()` is 
                                          # used for all instances of `C`, 
                                          # unless pobject is explicitly defined.
    P.__init__(self,None)
        self.name   = name
        self.pobject = pobject


p1  = P("1")
p2  = P("2")


c1a = C("c1a",p1)
c1b = C("c1b",p1)
c1c = C("c1c",p1)
c2a = C("c2a",p2)
c2b = C("c2b",p2)
c2c = C("c2c",p2)


print   id(c1a.pvar)
print   id(c1b.pvar)
print   id(c1c.pvar)
print   id(c2a.pvar)
print   id(c2b.pvar)
print   id(c2c.pvar)
print   id(p1.pvar)
print   id(p2.pvar)

print   id(c1a.name)
print   id(c1b.name)
print   id(c1c.name)
print   id(c2a.name)
print   id(c2b.name)
print   id(c2c.name)




print   "testing\n"

c1a.printname()
c1b.printname()
c1c.printname()
c2a.printname()
c2b.printname()
c2c.printname()


print   "\n"
c1a.name = "c1anewname"
c2b.name = "c2bnewname"


c1a.printname()
c1b.printname()
c1c.printname()
c2a.printname()
c2b.printname()
c2c.printname()


print "pvar\n"

print   c1a.pvar
print   c1b.pvar
print   c1c.pvar
print   c2a.pvar
print   c2b.pvar
print   c2c.pvar
print   p1.pvar
print   p2.pvar

print "\n"
c1a.pvar    = "newpvar1"

print   c1a.pvar
print   c1b.pvar
print   c1c.pvar
print   c2a.pvar
print   c2b.pvar
print   c2c.pvar
print   p1.pvar
print   p2.pvar

print "\n"
c2c.pvar    = "newpvar2"

print   c1a.pvar
print   c1b.pvar
print   c1c.pvar
print   c2a.pvar
print   c2b.pvar
print   c2c.pvar
print   p1.pvar
print   p2.pvar

с выводом

3077745184
3077745184
3077745184
3074414912
3074414912
3074414912
3077745184
3074414912
144028416
144028448
144028480
144028512
144028544
144028576
testing

c1a
c1b
c1c
c2a
c2b
c2c


c1anewname
c1b
c1c
c2a
c2bnewname
c2c
pvar

1
1
1
2
2
2
1
2


newpvar1
newpvar1
newpvar1
2
2
2
newpvar1
2


newpvar1
newpvar1
newpvar1
newpvar2
newpvar2
newpvar2
newpvar1
newpvar2

Кто-нибудь знает, почему это так?Я, наверное, не понимаю, как хорошо работает Python с этим __dict__ ...

Ответы [ 8 ]

5 голосов
/ 24 февраля 2011

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

Это не наследство.

Это совершенно другая концепция.

Ваши "общие переменные" - это просто объекты, которые могут быть видоизменены и иметь ссылки на другие объекты. Ничего интересного.

Наследование полностью отличается от этого.

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

Другой ответ правильный, ваш вопрос больше касается пространств имен и ссылок, чем наследования.

Все переменные в Python являются ссылками, а весь экземпляр объекта является пространством имен. Так что вы можете сделать:

class C():
    def __init__(self, x):
        self.x  = x

class Shared(object):
    def __init__(self, value):
        self.value  = value

# instances:
>>> shared1 = Shared(1)
>>> shared2 = Shared(2)
>>> c1 = C(shared1)
>>> c2 = C(shared1)
>>> c3 = C(shared2)
>>> c4 = C(shared2)
# c1 and c2 sharing a reference to shared1
>>> c1.x.value
1
>>> c2.x.value
1
# change c2.x will reflect on c1 
>>> c2.x.value = 3
>>> c1.x.value
3
# but not on c3, because shared1 and shared2 are distinct namespaces
>>> c3.x.value
2

UPDATE:

Но будьте осторожны, легко ошибиться:

>>> c4.x = 4
>>> c3.x.value
2
>>> c4.x.value
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: 'int' object has no attribute 'value'
>>> 

Я думаю, что современный уровень техники будет использовать properties , чтобы скрыть __shared_instance в частной переменной экземпляра - так что вы можете использовать c1.x вместо c1.x .value и избегайте опечаток, как в примере выше.

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

Я думаю, что ваш обходной путь выполним; Вы можете использовать свойства, чтобы упростить доступ к атрибутам P:

class P(object):
    def __init__(self,name='default',pvar=1):
        self.pname  = name
        self.pvar=pvar

class C(object):
    def __init__(self,name,pobject=P()):  #<-- The same default `P()` is 
                                          # used for all instances of `C`, 
                                          # unless pobject is explicitly defined.
        self.cname  = name
        self.pobject=pobject
    @property
    def pvar(self):
        return self.pobject.pvar
    @pvar.setter
    def pvar(self,val):
        self.pobject.pvar=val

c1=C('1')
c2=C('2')

c1 и c2 имеют одинаковые значения pobject:

print(c1.pvar)
# 1
c1.pvar=2

Обратите внимание, что изменение pvar до c1 меняет c2.pvar:

print(c2.pvar)
# 2

c3 имеет другое значение pobject:

c3=C('3',P())
print(c3.pvar)
# 1

Относительно дизайна ООП для психологического эксперимента (упомянуто в комментариях):

import Image

class Picture(object):
    def __init__(self,filename):
        self.filename = filename
        self.image=Image.open(filename)       

class Person(object):
    def __init__(self,name):
        self.name=name
        # other vital statistics associated with people as individuals here

class Trial(object):
    # A trial is composed of one person, one picture, and the places they look
    def __init__(self,person,picture,locations):
        self.person=person
        self.picture=picture
        self.locations = locations
    # put methods for analyzing where the person looked here

A Picture, безусловно, не является Person, и наоборот. И то же самое относится к Trial с. Поэтому ни один из этих классов не должен наследоваться друг от друга.

Каждый из этих классов имеет открытый (и, возможно, закрытый) интерфейс. Открытые методы и атрибуты должны быть свободно доступны из других классов. Таким образом, для экземпляра Trial, t, изображение должно быть доступно через t.picture.image. Пока вы обращаетесь только к публичным атрибутам и методам, все должно быть хорошо.

Для удобства вы можете использовать properties, чтобы связать атрибуты с компонентами-атрибутами. Например:

class Trial(object):
    ...
    @property
    def image(self):
        return self.picture.image

Но сократить это, скажем, Trial подклассом Picture противоречило бы фундаментальным принципам проектирования ООП.

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

Хорошо, я думаю, что вы можете перефразировать ваш вопрос следующим образом:

Как я могу расширить ООП Python, чтобы наследование работало на уровне объектов, а не классов?

Прежде всего - не связывайтесь с диктовками: Если вы просто копируете записи родительского диктата в child-dict, это работает для создания экземпляров, но изменяет любой из диктов.не будет автоматически обновлять записи во всех других диктовках.Присвоение нового значения атрибуту просто создаст новую ссылку, поэтому атрибут больше не будет указывать на тот же объект.

Решение состоит в том, чтобы заставить python искать атрибут в нужном месте, используя магию Python ...

class ProxyMixin (object):

    def __init__(self, parent):
        self.parent = parent

    def __getattribute__(self, name):
        if name != 'parent' and hasattr(self.parent, name):
            return getattr(self.parent, name)
        else:
            return object.__getattribute__(self, name)

    def __setattr__(self, name, val):
        if name != 'parent' and hasattr(self.parent, name):
            setattr(self.parent, name)
        else:
            object.__setattr__(self, name, val)

(см. Ссылку python по доступу к атрибуту )

Просто добавьте ProxyMixin в свой дочерний класс, и все будет в порядке.

class P:
    data = "shared data"
    def __init__(self, name):
        self.name = name
    def printname(self):
        print self.name

class C(P, ProxyMixin):
    def __init__(self, parent=None):
        if parent:
            ProxyMixin.__init__(self, parent)

Теперь вы можете динамически перезаписывать родительский объект в любое время, используя простое назначение:

c.parent = newparent
0 голосов
/ 26 февраля 2011

Наконец-то я нашел способ сделать это.

Ключевым моментом является отказ от цели получения экземпляров c с действительным полем pvar , поскольку это невозможно:

Поскольку этота же самая _ init _ () функция (существующая в классе P), которая обрабатывает для создания объектов pvar , невозможно создать pvar в случаях c , который будет указывать на pvar в экземпляре p , чтобы отразить его значение, и это также даст возможность изменить этозначение p pvar каждый раз, когда значение c * pvar будет меняться.Это создает слишком много противоречивых условий для проверки.

Следовательно, поскольку экземпляры c не могут иметь реального поля pvar , лучше всего настроить механизмуправление созданием (с _ setattr _ ) и доступом (с _ getattr _ ) к этим c казалось бы pvar объектов, чтобы создать иллюзию их существования.

class P(object):
    def __init__(self,pvar_arg,foo="^^^ "):
        self.pvar = pvar_arg
        self.cat  = foo
    def printname(self):
        print self.name

class C(P):

    def __init__(self,name,pobject,foo=''):
        self.__dict__['name'] = name
        P.__init__(self,None,pobject.cat+foo)
        C.dic[name] = pobject

    def __setattr__(self,xn,val):
        if xn!='pvar':
            self.__dict__[xn] = val
        elif self.name in C.dic:
            # During the creation of an instance c,
            # this condition is False because the instruction
            # C.dic[name] = pobject is  written after 
            # P.__init__(self,None,pobject.cat+foo).
            # Hence the value of pobject.pvar is preserved,
            # not changed with the value val being None
            # due to P.__init__(self,None,pobject.cat+foo)
            # that provokes self.pvar = pvar_arg and
            # consequently a call __setattr__(self,'pvar',None)
            C.dic[self.name].pvar = val

    def __getattribute__(self,xn):
        if xn=='pvar':
            return object.__getattribute__(C.dic[self.name],'pvar')
        else:
            return object.__getattribute__(self,xn)

    dic = {}


p1  = P("1")
p2  = P("2","QZX ")
print '--- p1  = P("1")  and  p2  = P("2","QZX ")  executed ---'
print "p1.__dict__  ==",p1.__dict__
print "p2.__dict__  ==",p2.__dict__
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar

c1a = C("c1a",p1,'sea')
c1b = C("c1b",p1,'mountain')
c1c = C("c1c",p1,'desert')
c2a = C("c2a",p2,'banana')
c2b = C("c2b",p2)
c2c = C("c2c",p2,'pear')
print '\n--- creations of c1a, c1b, c1c, c2a, c2b, c2c executed ---'
print "p1.__dict__  ==",p1.__dict__
print "p2.__dict__  ==",p2.__dict__
print "c1a.__dict__ ==",c1a.__dict__
print "c1b.__dict__ ==",c1b.__dict__
print "c1c.__dict__ ==",c1c.__dict__
print "c2a.__dict__ ==",c2a.__dict__
print "c2b.__dict__ ==",c2b.__dict__
print "c2c.__dict__ ==",c2c.__dict__
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print '(c1a.pvar, c1b.pvar, c1c.pvar)==',(c1a.pvar,c1b.pvar,c1c.pvar)
print '(c2a.pvar, c2b.pvar, c2c.pvar)==',(c2a.pvar,c2b.pvar,c2c.pvar)

c1a.pvar = "newpvar1"
print '\n--- c1a.pvar = "newpvar1"  executed ---'
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print '(c1a.pvar, c1b.pvar, c1c.pvar)==',(c1a.pvar,c1b.pvar,c1c.pvar)
print '(c2a.pvar, c2b.pvar, c2c.pvar)==',(c2a.pvar,c2b.pvar,c2c.pvar)

c2c.pvar = 45789
print '\n--- c2c.pvar = 45789  executed ---'
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print '(c1a.pvar, c1b.pvar, c1c.pvar)==',(c1a.pvar,c1b.pvar,c1c.pvar)
print '(c2a.pvar, c2b.pvar, c2c.pvar)==',(c2a.pvar,c2b.pvar,c2c.pvar)

result

--- p1  = P("1")  and  p2  = P("2","QZX ")  executed ---
p1.__dict__  == {'cat': '^^^ ', 'pvar': '1'}
p2.__dict__  == {'cat': 'QZX ', 'pvar': '2'}
p1.pvar== 1
p2.pvar== 2

--- creations of c1a, c1b, c1c, c2a, c2b, c2c executed ---
p1.__dict__  == {'cat': '^^^ ', 'pvar': '1'}
p2.__dict__  == {'cat': 'QZX ', 'pvar': '2'}
c1a.__dict__ == {'name': 'c1a', 'cat': '^^^ sea'}
c1b.__dict__ == {'name': 'c1b', 'cat': '^^^ mountain'}
c1c.__dict__ == {'name': 'c1c', 'cat': '^^^ desert'}
c2a.__dict__ == {'name': 'c2a', 'cat': 'QZX banana'}
c2b.__dict__ == {'name': 'c2b', 'cat': 'QZX '}
c2c.__dict__ == {'name': 'c2c', 'cat': 'QZX pear'}
p1.pvar== 1
p2.pvar== 2
(c1a.pvar, c1b.pvar, c1c.pvar)== ('1', '1', '1')
(c2a.pvar, c2b.pvar, c2c.pvar)== ('2', '2', '2')

--- c1a.pvar = "newpvar1"  executed ---
p1.pvar== newpvar1
p2.pvar== 2
(c1a.pvar, c1b.pvar, c1c.pvar)== ('newpvar1', 'newpvar1', 'newpvar1')
(c2a.pvar, c2b.pvar, c2c.pvar)== ('2', '2', '2')

--- c2c.pvar = 45789  executed ---
p1.pvar== newpvar1
p2.pvar== 45789
(c1a.pvar, c1b.pvar, c1c.pvar)== ('newpvar1', 'newpvar1', 'newpvar1')
(c2a.pvar, c2b.pvar, c2c.pvar)== (45789, 45789, 45789)

Замечания:

  1. атрибут name должен быть определен до инструкции

    P. init (self, None, pobject.cat + foo)

    потому что выполнение этой инструкции вызывает

    __setattr__(self,'pvar',"1"), которая сама выполняет инструкцию

    C.dic[self.name].pvar = "1" при выполнении, например,

    c1a = C("c1a",p1,'sea').

    AnHence, этот вызов требует self.name в качестве ключа для dic.

  2. Я ввел foo и cat до

    обосновать необходимость написания инструкции

    P. init (self, None, pobject.cat + foo)

    в противном случае, поскольку в экземплярах c фактически не определено pvar , этот метод не будет полезен.

  3. Существует две ситуациикоторый __setattr__ называется: при создании экземпляра и при изменении атрибутов существующего экземпляра.Когда экземпляр создается, значение pvar экземпляра p должно оставаться неизменным инструкцией C.dic[self.name].pvar = None.Следовательно, условие elif self.name in C.dic:

    Чтобы это условие дало правильный результат, инструкция C.dic[name] = pobject должна следовать за вызовом P.__init__(self,None,pobject.cat+foo)

.

РЕДАКТИРОВАТЬ 1

Я думаю, что лучше написать

def __setattr__(self,xn,val):
    if xn=='pvar':
        self.__class__.dic[self.name].pvar = val

, чем

def __setattr__(self,xn,val):
    if xn=='pvar':
        C.dic[self.name].pvar = val

В первом случае переводчик должен искать ссылкуклассу self C (то есть под именем '_ class _' ) в пространстве имен self.

Во втором случае интерпретатор должен искать ту же ссылку (но под именем 'C' ) в пространстве имен уровня, на котором классы P и C определены.

Это второе пространство имен может быть более большим, чем ограниченное пространство имен экземпляра.В первом случае имя '_ class _' ищется как ключ в словаре, реализующем пространство имен self.Во втором имя 'C' - это ключ, который ищется в словаре уровня, включающего классы P и C .

Идентичность этих двух объектов можно проверить с помощью функции id ()

.

.

EDIT 2

Для объекта dic существует еще одна возможность: вместо того, чтобы делать его атрибутом класса C , его можно определить во внешней области видимости класса C .Если этот внешний уровень является модулем, то dic является глобальным объектом.

class P(object):
    def __init__(self,pvar,foo="^^^ "):
        self.pvar   = pvar
        self.cat = foo
    def printname(self):
        print self.name

class C(P):

    def __init__(self,name,pobject,foo=''):
        self.__dict__['name'] = name
        P.__init__(self,None,pobject.cat+foo)
        dic[name] = pobject

    def __setattr__(self,xn,val):
        if xn!='pvar':
            self.__dict__[xn] = val
        elif self.name in dic:
            # During the creation of an instance c,
            # this condition is False because the instruction
            # dic[name] = pobject is  written after 
            # P.__init__(self,None,pobject.cat+foo).
            # Hence the value of pobject.pvar is preserved,
            # not changed with the value val being None
            # due to P.__init__(self,None,pobject.cat+foo)
            # that provokes self.pvar = pvar_arg and
            # consequently a call __setattr__(self,'pvar',None)
            dic[self.name].pvar = val

    def __getattribute__(self,xn):
        if xn=='pvar':
            return object.__getattribute__(dic[self.name],'pvar')
        else:
            return object.__getattribute__(self,xn)


dic = {}

Результат точно такой же

При этом dic теряет свою природу.

.

.

РЕДАКТИРОВАТЬ 3

Наконец, есть еще один способ: вместо создания иллюзорного атрибута pvar для каждого экземпляра c с помощью функций __setattr__ и __getattribute__ лучше по моему мнению, использовать функцию со словарем dic в качестве аргумента по умолчанию, и это заменит их.

class P(object):
    def __init__(self,pvar,foo="^^^ "):
        self.pvar   = pvar
        self.cat = foo
    def printname(self):
        print self.name

class C(P):
    def __init__(self,name,pobject,foo=''):
        P.__init__(self,None,pobject.cat+foo)
        self.__dict__['name'] = name
        del self.pvar
        self.pvar(pobject)

    def pvar(self,x = None,dic = {}):
        if x.__class__==P: # a pobject
            dic[self.name] = x
        elif x: # a value
            dic[self.name].pvar = x
        else: # to return value
            return dic[self.name].pvar

p1  = P("1")
p2  = P("2","QZX ")
print '--- p1  = P("1")  and  p2  = P("2","QZX ")  executed ---'
print "p1.__dict__  ==",p1.__dict__
print "p2.__dict__  ==",p2.__dict__
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar

c1a = C("c1a",p1,'sea')
c1b = C("c1b",p1,'mountain')
c1c = C("c1c",p1,'desert')
c2a = C("c2a",p2,'banana')
c2b = C("c2b",p2)
c2c = C("c2c",p2,'pear')
print '\n--- creations of c1a, c1b, c1c, c2a, c2b, c2c executed ---'
print "p1.__dict__  ==",p1.__dict__
print "p2.__dict__  ==",p2.__dict__
print "c1a.__dict__ ==",c1a.__dict__
print "c1b.__dict__ ==",c1b.__dict__
print "c1c.__dict__ ==",c1c.__dict__
print "c2a.__dict__ ==",c2a.__dict__
print "c2b.__dict__ ==",c2b.__dict__
print "c2c.__dict__ ==",c2c.__dict__
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print   '(c1a.pvar(),c1b.pvar(),c1c.pvar())==',(c1a.pvar(),c1b.pvar(),c1c.pvar())
print   '(c2a.pvar(),c2b.pvar(),c2c.pvar())==',(c2a.pvar(),c2b.pvar(),c2c.pvar())

c1a.pvar("newpvar1")
print '\n--- c1a.pvar("newpvar1")  executed ---'
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print   '(c1a.pvar(),c1b.pvar(),c1c.pvar())==',(c1a.pvar(),c1b.pvar(),c1c.pvar())
print   '(c2a.pvar(),c2b.pvar(),c2c.pvar())==',(c2a.pvar(),c2b.pvar(),c2c.pvar())

c2c.pvar(45789)
print '\n--- c2c.pvar(45789) ---'
print 'p1.pvar==',p1.pvar
print 'p2.pvar==',p2.pvar
print   '(c1a.pvar(),c1b.pvar(),c1c.pvar())==',(c1a.pvar(),c1b.pvar(),c1c.pvar())
print   '(c2a.pvar(),c2b.pvar(),c2c.pvar())==',(c2a.pvar(),c2b.pvar(),c2c.pvar())

Результаты такие же, только использование c.pvar () немного отличается:

--- p1  = P("1")  and  p2  = P("2","QZX ")  executed ---
p1.__dict__  == {'cat': '^^^ ', 'pvar': '1'}
p2.__dict__  == {'cat': 'QZX ', 'pvar': '2'}
p1.pvar== 1
p2.pvar== 2

--- creations of c1a, c1b, c1c, c2a, c2b, c2c executed ---
p1.__dict__  == {'cat': '^^^ ', 'pvar': '1'}
p2.__dict__  == {'cat': 'QZX ', 'pvar': '2'}
c1a.__dict__ == {'cat': '^^^ sea', 'name': 'c1a'}
c1b.__dict__ == {'cat': '^^^ mountain', 'name': 'c1b'}
c1c.__dict__ == {'cat': '^^^ desert', 'name': 'c1c'}
c2a.__dict__ == {'cat': 'QZX banana', 'name': 'c2a'}
c2b.__dict__ == {'cat': 'QZX ', 'name': 'c2b'}
c2c.__dict__ == {'cat': 'QZX pear', 'name': 'c2c'}
p1.pvar== 1
p2.pvar== 2
(c1a.pvar(),c1b.pvar(),c1c.pvar())== ('1', '1', '1')
(c2a.pvar(),c2b.pvar(),c2c.pvar())== ('2', '2', '2')

--- c1a.pvar("newpvar1")  executed ---
p1.pvar== newpvar1
p2.pvar== 2
(c1a.pvar(),c1b.pvar(),c1c.pvar())== ('newpvar1', 'newpvar1', 'newpvar1')
(c2a.pvar(),c2b.pvar(),c2c.pvar())== ('2', '2', '2')

--- c2c.pvar(45789) ---
p1.pvar== newpvar1
p2.pvar== 45789
(c1a.pvar(),c1b.pvar(),c1c.pvar())== ('newpvar1', 'newpvar1', 'newpvar1')
(c2a.pvar(),c2b.pvar(),c2c.pvar())== (45789, 45789, 45789)

Обратите внимание, что в этом последнем коде P экземпляры не могут быть значениями C ', потому что объект передан c Метод .pvar () никогда не будет рассматриваться как значение.

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

r6d9, пожалуйста, когда вы пишете обновление, вы должны поставить дату и час словом UPDATE.Он начинает усложняться, чтобы следовать всем этим

.

.

Относительно этого вашего кода:

def replaceinstance(parent,child):
    for item in parent.__dict__.items():
        child.__dict__.__setitem__(item[0],item[1])
        print item

class P(object):
    def __init__(self,pvar):
        self.pvar   = pvar
    def printname(self):
        print self.name

class C(P):
    def __init__(self,name,pobject):
        P.__init__(self,None)
        replaceinstance(pobject,self)
        self.name   = name

он может быть заменен этимодин:

class P(object):
    def __init__(self,pvar):
        self.pvar   = pvar
    def printname(self):
        print self.name

class C(P):
    def __init__(self,name,pobject):
        P.__init__(self,None)
        self.pvar = pobject.pvar
        self.name   = name

но кажется простым

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

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

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

Если атрибут там не найден, а класс экземпляра имеет атрибут с таким именем, поиск продолжается с атрибутами класса.

http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

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

В следующем коде:

  • Для класса P и экземпляра c:

имя 'dataclass' и объект dataclass действительно принадлежат пространству имен класса P и только ВНУТРИ принадлежат пространству имен экземпляра c: когда вызывается c.dataclass, это фактически c.__class__.dataclass, чтодостигается путем поиска, описанного выше.

  • Но в экземпляре cc класса PP имя 'data' , которое принадлежитP пространство имен класса, назначается (связывается) по определению, встречающемуся в __init__(), новому data объекту, созданному в пространстве имен экземпляра c.

Следовательно, единственное решениеполучить значение data класса, чтобы вызвать его по реальной ссылке, либо PP.data, либо cc.__class__.data.

class   P:
    dataclass    = "shareddata"

    def __init__(self,newdata):
        self.data   = newdata

    def printname(self):
        print   self.name

c = P(1)

print 'P.__dict__.keys()==',P.__dict__.keys()
print 'c.__dict__.keys()==',c.__dict__.keys()
print  
print 'c.data==',c.data
print 'c.dataclass==',c.dataclass


print 

class   PP:
    data    = "shareddata"

    def __init__(self,newdata):
        self.data   = newdata

    def printname(self):
        print   self.name

cc = PP(2)
print 'PP.__dict__.keys()==',PP.__dict__.keys()
print 'cc.__dict__.keys()==',cc.__dict__.keys()
print
print 'cc.data==',cc.data
print 'PP.data==',PP.data
print 'cc.__class__.data==',cc.__class__.data

result

P.__dict__.keys()== ['dataclass', '__module__', 'printname', '__init__', '__doc__']
c.__dict__.keys()== ['data']

c.data== 1
c.dataclass== shareddata

PP.__dict__.keys()== ['__module__', 'data', 'printname', '__init__', '__doc__']
cc.__dict__.keys()== ['data']

cc.data== 2
PP.data== shareddata
cc.__class__.data== shareddata

.

Примечание:

dir ([объект])

С помощью аргумента попытайтесь вернуть список допустимых атрибутов для этого объекта.

Если объект не предоставляет dir (), функция изо всех сил пытается собрать информацию из атрибута dict объекта, если он определен, и из объекта его типа.

Механизм dir () по умолчанию ведет себя по-разному с различными типами объектов, так как он пытается получить наиболее важную, а не полную информацию:

• Если объект является типом или классомобъект, список содержит имена его атрибутов и рекурсивные атрибуты его баз.

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

http://docs.python.org/library/functions.html#dir

.

Следовательно, использование dir(ob) для отображения атрибутов объекта obловушка, потому что она отображает больше атрибутов, чем принадлежащих строго объекту.

Другими словами, __ dict __ является реальной вещью, тогда как dir () даетприборная панель, в некотором смысле.

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

Я потерялся во всех этих разнообразных ответах.

Но я думаю, что то, что вам нужно, выражается в следующем коде:

class P:
    pvar=1                    # <--- class attribute
    def __init__(self,name):
        self.cname  = name

class C(P):
    def __init__(self,name):
        self.cname  = name


c1=C('1')
c2=C('2')

print
print "C.pvar ==",C.pvar,'  id(C.pvar) ==',id(C.pvar)
print "c1.pvar==",c1.pvar,'  id(c1.pvar)==',id(c1.pvar)
print "c2.pvar==",c2.pvar,'  id(c2.pvar)==',id(c2.pvar)

print
C.pvar = [1,2]
print "instruction   C.pvar = [1,2]   executed"
print "C.pvar ==",C.pvar,'  id(C.pvar) ==',id(C.pvar)
print "c1.pvar==",c1.pvar,'  id(c1.pvar)==',id(c1.pvar)
print "c2.pvar==",c2.pvar,'  id(c2.pvar)==',id(c2.pvar)

print
c2.__class__.pvar = 'sun'
print "instruction   c2.__class__.pvar = 'sun'   executed"
print "C.pvar ==",C.pvar,'  id(C.pvar) ==',id(C.pvar)
print "c1.pvar==",c1.pvar,'  id(c1.pvar)==',id(c1.pvar)
print "c2.pvar==",c2.pvar,'  id(c2.pvar)==',id(c2.pvar)

print
c2.pvar = 145
print "instruction   c2.pvar = 145   executed"
print "C.pvar ==",C.pvar,'  id(C.pvar) ==',id(C.pvar)
print "c1.pvar==",c1.pvar,'  id(c1.pvar)==',id(c1.pvar)
print "c2.pvar==",c2.pvar,'  id(c2.pvar)==',id(c2.pvar)

результат

C.pvar == 1   id(C.pvar) == 10021768
c1.pvar== 1   id(c1.pvar)== 10021768
c2.pvar== 1   id(c2.pvar)== 10021768

instruction   C.pvar = [1,2]   executed
C.pvar == [1, 2]   id(C.pvar) == 18729640
c1.pvar== [1, 2]   id(c1.pvar)== 18729640
c2.pvar== [1, 2]   id(c2.pvar)== 18729640

instruction   c2.__class__.pvar = 'sun'   executed
C.pvar == sun   id(C.pvar) == 18579136
c1.pvar== sun   id(c1.pvar)== 18579136
c2.pvar== sun   id(c2.pvar)== 18579136

instruction   c2.pvar = 145   executed
C.pvar == sun   id(C.pvar) == 18579136
c1.pvar== sun   id(c1.pvar)== 18579136
c2.pvar== 145   id(c2.pvar)== 10022024

Я имею в виду, что вы должны знать, что нужно изменить посредством инструкции, подразумевающей непосредственно имя экземпляра (а не путем изменения, подразумевающего только имя родительского класса), атрибут класса pvar , пока продолжает использоваться всеми экземплярами P , вы должны написать

c2.__class__.pvar = something 

а не

c2.pvar =something

Обратите внимание, что C является классом, эффективно наследующим от родительского класса P

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