Pull to refresh

Comments 17

Неплохо бы указать откуда взялся printAttr, поскольку он не входит в стандартную библиотеку Python.
Согласен, но суть не в ней. Сейчас добавлю
Принимая во внимание концепцию Питона, что все открыто для всех, почему бы не сделать доступ к «классным» атрибутам только через __class__ или его аналог.

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

1. Точно помнить классовый он или инстансный.
2. Писать что-то типа
 Drum.__class__. vtype


Во-вторых, не совсем понятно, чего вы ожидаете, меняя атрибут инстанса в рантайме? Изменение атрибута класса? Эксепшен?

В-третьих, если уж вы выкладываете код, то хоть немного его приведите в порядок в соответствии с pep-8.

Iowa = Vessel("Iowa")
Drum=Vessel("Drum")
printAttr(Iowa, Drum)


это

iowa = Vessel("Iowa")
drum = Vessel("Drum")
print_attr(Iowa, Drum)
1. Вам все равно нужно знать какой это атрибут, чтобы случайно не присвоить ему что-либо, и не попасть в ситуацию мной описанную. И я совсем не против читаемости, но не в ущерб «безопасности»
2. Я ожидаю того, что описано в документации — изменения значения атрибута у всех инстансов, как это происходит при обращении через __class__
изменения значения атрибута у всех инстансов, как это происходит при обращении через __class__

А у кого тогда должно обновиться свойство в таких случаях?

class x():
    prop=1

class y(x):
    pass

inst = y()
inst.prop = 555
inst.__class__.prop = 666


ЗЫ.
Почти во всех языках (а может и во всех), если можно получить доступ к свойству класса через свойство экземпляра, то присваивание значения через свойство экземпляра присваивает/обновляет именно свойство экземпляра, а не свойство объекта/класса, откуда унаследовалось.
В любом языке можно наткнуться на неприятные сюрпризы, если прийти в него из другого языка, ожидая, что здесь всё будет точно так же. Особенно, если сразу начать писать продакшн-код на новом языке, толком его не освоив. Но нормальные программисты всё-таки сначала учат язык, а потом пишут на нём продакшн-код.
То, о чём вы пишете в этой статье, рассказывают в любом вводном мануале/ролике по ООП в питоне.
Никто и не ожидает, что все будет также. Просто некоторые моменты никак не ожидаешь.
Не могли бы вы указать на вводный мануал, где подобное поведение описывается, я пересмотрел штуки три и нигде явно этого не было.
А чего тут особенного? Обычное перекрытие свойств класса свойствами объекта. Точно так же, как и при наследовании и перекрытии свойств одного класса другим классом.
В javascript кажется так же.
При наследовании атрибуты не меняют сущности — они как были свойствами объекта так и остаются. В данном случае происходит завуалированная подмена, причем не описанная в документации.
Так я и написал:
Обычное перекрытие свойств класса свойствами объекта.

Это не подмена. Просто пока не создано свойство объекта с помощью присваивания ему значения, получаешь доступ на чтение к свойству класса или его суперклассам (что «ближе» к объекту). В JavaScript такое же поведение.
причем не описанная в документации.

Описанная. Есть минимум в одном месте:
Class instances

A class instance is created by calling a class object (see above). A class instance has a namespace implemented as a dictionary which is the first place in which attribute references are searched. When an attribute is not found there, and the instance’s class has an attribute by that name, the search continues with the class attributes. If a class attribute is found that is a user-defined function object, it is transformed into an instance method object whose __self__ attribute is the instance. Static method and class method objects are also transformed; see above under “Classes”. See section Implementing Descriptors for another way in which attributes of a class retrieved via its instances may differ from the objects actually stored in the class’s __dict__. If no class attribute is found, and the object’s class has a __getattr__() method, that is called to satisfy the lookup.

Attribute assignments and deletions update the instance’s dictionary, never a class’s dictionary. If the class has a __setattr__() or __delattr__() method, this is called instead of updating the instance dictionary directly.

Class instances can pretend to be numbers, sequences, or mappings if they have methods with certain special names. See section Special method names.

Special attributes: __dict__ is the attribute dictionary; __class__ is the instance’s class.

И поведение это можно менять (инфа из выше):
If the class has a __setattr__() or __delattr__() method, this is called instead of updating the instance dictionary directly.

Еще чего то можно мутить через дескрипторы.
Да, если начать разбираться с namespace и dict, то становится примерно понятно как и почему так оно все работает. Хотелось бы чтобы подобное, на мой взгляд нетипичное поведение, было бы описано более четко.

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


Читаем абзац из раздела 7.2 Инструкции присваивания «Справочного руководства по языку Python» (выделено жирным моё):


7.2 Инструкции присваивания

Присваивание объекта одной цели рекурсивно определяется следующим образом.

  • Если целью является ссылка на атрибут: в ссылке вычисляется первичное выражение. Оно должно выдать объект с присваиваемыми атрибутами; если это не так, возбуждается исключение TypeError. Затем этому объекту предлагается присвоить данному атрибуту присваиваемый объект; если он не может выполнить присваивание, он возбуждает исключение (обычно, но не обязательно, AttributeError).

    Примечание: Если объект является экземпляром класса и ссылка на атрибут появляется с обеих сторон оператора присваивания, выражение справа, a.x, может получить доступ к атрибуту экземпляра или (если атрибут экземпляра не существует) к атрибуту класса. Цель слева a.x всегда устанавливается в атрибут экземпляра, при необходимости создавая его. Таким образом, два вхождения a.x не обязательно ссылаются на один и тот же атрибут: если выражение справа ссылается на атрибут класса, выражение слева создаёт новый атрибут экземпляра в качестве цели присваивания:
    class Cls:
    x = 3             # переменная класса
    inst = Cls()
    inst.x = inst.x + 1   # записывает 4 в inst.x, оставляя Cls.x равным 3


    Это описание не обязано применяться к атрибутам-дескрипторам, вроде свойств, созданных с помощью property().


...
Вот это да.В этот раздел я точно не заглядывал — спасибо.
Если начать разбираться с namespace-ами, то подобное поведение становится понятным.
Однако для программистов, кто раньше работал с нормальными языками, это по меньшей мере кажется странным.

Вы не могли бы, пожалуйста, рассказать какие языки нормальные и какие критерии определяют нормальность языка раз уж python с его огромным комьюнити такие «ненормальные»?
Нормальный это безусловно понятие субъективное. Для меня нормальным языком является тот, который не порождает двоякости трактования, и который запрещает делать то, что не разрешено. По своему опыту знаю, что если при разработке системы классов что-то не скрыто — это позже будет использовано и самым извращенным способом.
Я предложу такое определение нормальности: если языком успешно пользуются тысячи если не миллионы людей — это нормально. Если человек не разобравшись в документации и в поведении о котором знает, надеюсь, любой джун, идет в интернет писать что язык плохой — вот это не нормально. У всех языков есть свои особенности — если слушать Вас, то javascript вообще на костре должен гореть сами знаете где, а .net и java — наше все.
Интересно какое место в вашей системе координат занимают функциональные языки?
Количество пользователей языка ничего не говорил о нормальности. Анекдот про мух знаете :)
Нормальность языка можно и нужно оценивать только в контексте поставленных задач.
И где вы нашли, что я написал, что язык плохой?
И таки да для разработки больших и сложных систем, где работает пара десятков программистов из разных отделов, я выберу и выбираю .net c джавой, жаль что Delphi помер.
Sign up to leave a comment.

Articles