25

Примечание: Я не говорю о предотвращении перезаписи переменной. Я говорю о предотвращении изменения памяти, к которой относится переменная, и о любой памяти, которая может быть достигнута оттуда, следуя вложенным контейнерам.Почему Python решил отказаться от постоянных ссылок?

У меня есть большая структура данных, и я хочу предоставить ее другим модулям только для чтения. Единственный способ сделать это в Python - это глубокое копирование отдельных фрагментов, которые я хотел бы разоблачить, - в нашем случае - слишком дорого.

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

Любые идеи были бы оценены.


Хотя ответы полезны, я не видел ни одной причины, почему сопзИте будет либо трудно реализовать или неосуществимыми в Python. Я думаю, что «не-Pythonic» также будет считаться веской причиной, но действительно ли это? Python выполняет скремблирование переменных частного экземпляра (начиная с __), чтобы избежать случайных ошибок, и const не похоже на то, что отличается от духа.


EDIT: Я просто предложил очень скромную щедрость. Я ищу немного подробней о том, почему Python закончил без const. Я подозреваю, что причина в том, что его действительно трудно реализовать, чтобы работать отлично; Я хотел бы понять, почему так сложно.

ответ

10

В PEP 351 Барри Варшава предложила протокол для «замораживания» любой изменяемой структуры данных, аналогично тому, как frozenset создает неизменяемый набор. Замороженные структуры данных могут быть хешируемыми и поэтому могут использоваться как ключи в словарях.

Предложение было discussed on python-dev, с Raymond Hettinger's criticism самым подробным.

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

+0

Спасибо, очень полезно. Хотя я просто ограничивал свой интерес 'const', чтобы избежать ошибок, этот поток охватывал более« полезную »/« широкую »концепцию' concept' как хешируемого типа данных. Тем не менее, очень интересно читать. – max

8

Существует множество вопросов дизайна по любому языку, ответ на большинство из которых «просто потому, что». Довольно ясно, что такие константы противоречат идеологии Python.


Вы можете сделать только для чтения атрибута класса, хотя, используя descriptors. Это не тривиально, но это не очень сложно. Способ его работы заключается в том, что вы можете создавать свойства (объекты, которые выглядят как атрибуты, но вызов метода при доступе) с использованием декоратора property; если вы создадите getter, но не свойство setter, вы получите атрибут только для чтения. Причиной для программирования метакласса является то, что с __init__ получает полностью сформированный экземпляр класса . Фактически вы не можете установить атрибуты на то, что вы хотите на этом этапе! Вместо этого вы должны установить их при создании класса, а это значит, что вам нужен метакласс.

код из this recipe:

# simple read only attributes with meta-class programming 

# method factory for an attribute get method 
def getmethod(attrname): 
    def _getmethod(self): 
     return self.__readonly__[attrname] 

    return _getmethod 

class metaClass(type): 
    def __new__(cls,classname,bases,classdict): 
     readonly = classdict.get('__readonly__',{}) 
     for name,default in readonly.items(): 
      classdict[name] = property(getmethod(name)) 

     return type.__new__(cls,classname,bases,classdict) 

class ROClass(object): 
    __metaclass__ = metaClass 
    __readonly__ = {'a':1,'b':'text'} 


if __name__ == '__main__': 
    def test1(): 
     t = ROClass() 
     print t.a 
     print t.b 

    def test2(): 
     t = ROClass() 
     t.a = 2 

    test1() 
+0

Я ценю эту информацию, это интересно. Но это не касается проблемы, к которой я имел в виду вообще: предположим, что атрибутом является контейнер. Несмотря на то, что атрибут неизменен, я все еще могу изменить все его элементы. Кроме того, могу ли я изменить этот рецепт, чтобы передать постоянный словарь (на неглубоком уровне)? – max

+1

Вопрос о вашем примере кода: что помешало бы кому-то делать t .__ readonly __ ['a'] = 2 и изменять данные? –

+2

@Max: если вы хотите вложенные структуры только для чтения, тогда да, вам придется реализовать эту идею один раз за уровень, пока не нажмете неизменяемые. Вы также можете написать свой собственный 'dict' класс для обработки словарей, предназначенных только для чтения, путем изменения' getitem' и 'setitem'. Это будет не очень хорошо (или безупречно), потому что это не так, как нужно писать Python. – katrielalex

13

Это так же, как с частными методами: as consenting adults авторы кода должны согласовать интерфейс без необходимости применения силы. Потому что действительно действительно, применяющий договор, является hard, и, делая это, полуподобный путь ведет к хакерскому коду в изобилии.

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

+2

Я не беспокоюсь о хаках. Я просто помогаю себе избегать ошибок. Когда я передаю определенный контейнер, я вызываю различные функции, которые могут (случайно) изменить его содержимое. Я хотел бы узнать об этом. – max

+0

Конечно, если язык, на котором вы используете, имеет способы реально обеспечить соблюдение этих правил, то эти функции должны использоваться! –

+1

@max Почему сосредоточиться на случайных мутациях как на важном источнике ошибок? Я не могу вспомнить ни одного раза, когда у меня была ошибка, потому что я случайно изменил то, что должно было быть постоянным. Тем не менее, я помню буквально сотни раз, когда мне приходилось исправлять код, потому что аннотации const не соглашались. – 2010-11-20 11:15:50

1

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

Для таких взрослых существует определенная безопасность, предоставляемая Enthought's Traits.

Вы можете посмотреть Constant and ReadOnly черты.

1

Для некоторых дополнительных мыслей, есть подобный вопрос, заданный о Java здесь:
Why is there no Constant feature in Java?

Когда спрашивают, почему Питон решил против постоянных ссылок, я думаю, что это полезно думать о том, как они будут реализованы на языке. Должен ли Python иметь специальную декларацию, const, чтобы создать ссылки переменных, которые нельзя изменить? Почему бы не позволить объявлять переменные float/int/what then then, они наверняка помогут предотвратить ошибки программирования. Пока мы это делаем, добавляем модификаторы класса и метода, такие как protected/private/public/etc. поможет принудительно выполнить проверку типа компиляции против незаконного использования этих классов. ... довольно скоро мы потеряли красоту, простоту и элегантность, которые являются Python, и мы пишем код в виде какого-то ублюдкового ребенка C++/Java.

Python также в настоящее время передает все по ссылке. Это будет своего рода специальная модификация pass-by-reference-but-flag-it-to-prevent ... довольно частный случай (и, как указывает Tao of Python, просто «не-Pythonic»).

Как уже упоминалось ранее, без изменения языка этот тип поведения может быть реализован с помощью дескрипторов классов &. Это может не помешать модификации определенного хакера, но мы соглашаемся с взрослыми. Python не обязательно принял решение против предоставления этого в качестве входящего в комплект модуля («батарейки включены») - спроса на него просто не хватало.

+0

Python не использует pass-by-reference. Он использует «ссылки», но передает их по значению. Сравните 'def foo (x) {x = 10; } bar = 5; Foo (бар); print bar' (Python с греховными фигурными скобками, поскольку комментарии не превалируют строки/отступы, pass-by-value) с 'void foo (int & x) {x = 10; } /*...*/ int bar = 5; Foo (бар); cout << bar; '(C++, pass-by-reference). – delnan

+0

Спасибо за разъяснение. – Gerrat