Чому клас __dict__ є mappingproxy?


Відповіді:


80

Це допомагає інтерпретатору запевнити, що ключі для атрибутів та методів класу можуть бути лише рядками.

В іншому, Python є "мовою дорослих, що погоджується", що означає, що користувачі можуть розкривати та змінювати диктовки для об'єктів. Однак у випадку атрибутів та методів класу на рівні класів, якщо ми можемо гарантувати, що ключі є рядками, ми можемо спростити та пришвидшити загальний код справи для пошуку атрибутів та методів на рівні класу. Зокрема, __mro__ логіка пошуку для класів нового стилю спрощується та пришвидшується, вважаючи, що клавіші dict класу є рядками.


29
І для цікавих: mappingproxyробить class.__dict__лише для читання, так що це class.__setattr__залишається лише способом встановлення атрибутів класу, і саме цей метод застосовує обмеження .
Мартін Пітерс

2
Це також актуально для тих, хто перевизначає type.__setattr__(що, сподіваємось, / можливо, досить невеликий набір), оскільки ви не можете писати __dict__; ви повинні використовувати super().
Kevin

4
Це також гарантує, що якщо ви дасте класу новий магічний метод, Python може оновити відповідний слот рівня C. Якщо ви обійдете це, використовуючи щось на зразок gc.get_referents(FooClass.__dict__)[0]['__eq__'] = eqmethod, екземпляри FooClassможуть насправді не використовуватись eqmethodдля ==порівняння.
user2357112 підтримує Моніку

Думаю, кожному, хто хоче серіалізувати похідний клас у Json, просто не пощастило: /
Basic

@MartijnPieters Щоб пояснити, така природа лише для читання є причиною того, що атрибути класу можуть бути перепризначені лише через доступ через сам клас? І чому спроба повторно призначити атрибут класу через доступ через екземпляр призводить до створення нового атрибута екземпляра з тим самим іменем, що і атрибут класу?
soporific312

9

Картографуючий проксі - це просто дикт без __setattr__методу.

Ви можете ознайомитися з цим кодом і звернутися до нього.

from types import MappingProxyType
d={'key': "value"}
m = MappingProxyType(d)
print(type(m)) # <class 'mappingproxy'>

m['key']='new' #TypeError: 'mappingproxy' object does not support item assignment

mappingproxy - це версія Python 3.3. Наступний код показує типи dict:

class C:pass
ci=C()
print(type(C.__dict__)) #<class 'mappingproxy'>
print(type(ci.__dict__)) #<class 'dict'>

2

Оскільки mappingproxyтип Python 3.3 було перейменовано з dictproxy. На цю тему відбулася цікава дискусія .

Трохи важко знайти документацію для цього типу, але документація для методу vars це прекрасно описує (хоча деякий час це не документувалось ):

Такі об'єкти, як модулі та екземпляри, мають __dict__ атрибут, що оновлюється ; однак інші об'єкти можуть мати обмеження на запис щодо своїх __dict__атрибутів (наприклад, класи використовують type.MappingProxyType для запобігання прямим оновленням словника).

Якщо вам потрібно призначити новий атрибут класу, який ви могли б використовувати setattr. Варто зазначити, що mappingproxyJSON не можна серіалізувати. Перевірте проблему, щоб зрозуміти, чому.


Також історія цього типу досить цікава:

  • Python 2.7: type(A.__dict__) повертається <type 'dict'>як type(dict()), і можливо призначити нові атрибути через __dict__, наприклад A.__dict__['foo'] = 'bar'.

  • Python 3.0 - 3.2: type(A.__dict__) повертається <class 'dict_proxy'>, різниця тепер трохи чіткіша. Спроба призначити новий атрибут дає TypeError. Була спроба додати dictproxyяк загальнодоступний вбудований тип.

  • Python 3.3: додає <class 'mappingproxy'>тип, описаний вище.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.