Не понимаю, почему точечная запись не работает для составных объектов - PullRequest
0 голосов
/ 01 октября 2019

Вот то, что я пытаюсь

# This is how I understood composition to work
class A():
    def __init__(self):
        self.attr1 = ''
        self.attr2 = 0
        self.attr3 = ''

    def some_update(self, newval):
        self.attr3 = newval

class B():
    def __init__(self):
        self.attr4 = ''
        self.sub1 = A()
    self.sub2 = A()
        self.sub2.attr1 = subspecialval

    def different_update(self, newval1, newval2)
        self.sub1.attr2 = newval1
        self.sub2.attr2 = newval2

def __main__():
    my_obj = B()
    B.sub1.attr = 'some string'               # This doesn't work
    setattr(B, 'sub1.attr1', 'some string')   # This works
    print(B.sub1.attr1)                       # This doesn't work
    print(getattr(B, 'sub1.attr1'))           # This works

if __name__ == '__main__':
    main()

Вопрос в том, почему точечная нотация не распространяется на составные классы (B), но getattr / setattr способны правильно определять иерархию классов? Я предполагал, что каждая точка будет расширяться до следующего уровня объекта в иерархии, но, похоже, это не так

Ответы [ 2 ]

1 голос
/ 01 октября 2019

getattr / setattr способны правильно согласовывать иерархию классов?

Это не так.

setattr(B, 'sub1.attr1', 'some string') устанавливает буквальный атрибут 'sub1.attr1'. Ничто не пересекается или не согласовывается.

Имейте в виду, вы устанавливаете атрибут для класса (B), а не для экземпляра (my_obj). my_obj.sub1.attr сработало бы.

class A:
    def __init__(self):
        self.attr2 = ''

class B:
    def __init__(self):
        self.a = A()


B.a.attr1 = ''

AttributeError: type object 'B' has no attribute 'a'

С другой стороны,

b = B()
b.a.attr1 = 'attr1'
print(b.a.attr1)

Работает.


Что делает setattr(B, 'a.attr1', 'new')?

Он устанавливает буквальный атрибут 'a.attr1 для самого B класса . Ничто не передается или не согласовывается.

setattr(B, 'a.attr1', 'new')
print(B.__dict__)

Выходы

{'__module__': '__main__', '__init__': <function B.__init__ at 0x00000188F1EB9F28>, 
 '__dict__': <attribute '__dict__' of 'B' objects>, 
 '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None, 
 'a.attr1': 'new'}
0 голосов
/ 02 октября 2019

У вас есть фундаментальное недопонимание классов и Python.

B.sub1.attr = 'some string' не работает

B - это тип <class 'B'>. Нет атрибута sub1 в типе B.

Однако

Я полагаю, вы ожидали тип <class 'A'>, вы можете сделать это, определив этот атрибут на уровне класса:

>>> class A:
...     pass
...
>>> class B:
...     sub1 = A()
...
>>> B.sub1
<__main__.A object at 0x7fa2e1d1c490>

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

>>> b = B()
>>> b.sub1
<__main__.A object at 0x7fa2e1d1c490>

setattr(B, 'sub1.attr1', 'some string') работает

setattr всегда будет работать, если первый параметр является экземпляромкласса. Это заставляет атрибут быть доступным. Он создает атрибуты в объектах класса.

B.sub1.attr1 не работает. В то время как getattr(B, 'sub1.attr1') делает

Это самый странный из всех, и я тоже не знал. Это сводится к вызову на setattr(B, 'sub1.attr1', 'some string') как раз перед. Как уже говорилось, это заставляет атрибут.

Я считаю, что происходит то, что вы не можете получить доступ к этому атрибуту через синтаксис Python, поскольку имя атрибута на самом деле содержит точку sub1.attr1. Но вы можете передать необработанную строку в качестве имени атрибута в getattr, и именно поэтому он работает.

Однако

Что вы, вероятно, хотели сделать в этом сценарии, это установить атрибут вэкземпляр класса 'A'.

>>> class A:
...     def __init__(self):
...             self.attr1 = 'attr A'
...
>>> class B:
...     pass
...
>>> setattr(B, 'sub1', A())
>>> B.sub1.attr1
'attr A'

Но в этот момент атрибут sub1.attr1 не существует!

>>> getattr(B, 'sub1.attr1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'B' has no attribute 'sub1.attr1'

Заключение

Выделать очень странные вещи со встроенными функциями getattr и setattr. Это хорошо, если вы учитесь, но для успешного использования таких функций необходимо знать, что происходит в языке.

При достаточном количестве магии имя атрибута может действительно иметь . s.

...