2013-09-29 6 views
9

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

Я довольно новичок в Python, в настоящее время экспериментирую с языком. Я заметил, что все встроенные типы не могут быть расширены другими членами. Я хотел бы, например, добавить метод each к list тип, но это было невозможно. Я понимаю, что он разработан таким образом по соображениям эффективности и что большинство встроенных типов реализованы в C.

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

class MyList(list): 
    def each(self, func): 
     for item in self: 
      func(item) 
list = MyList 

my_list = list((1,2,3,4)) 
my_list.each(lambda x: print(x)) 

Выход:

1 
2 
3 
4 

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

Только проблема, с которой я столкнулся сейчас, заключается в том, что при создании экземпляра list по его литеральной форме (то есть [1,2,3,4]) он по-прежнему будет использовать исходный конструктор списка (или он?). Есть ли способ отменить это поведение? Если ответ отрицательный, знаете ли вы какой-либо другой способ, позволяющий пользователю расширять встроенные типы? (так же, как javascript позволяет расширять встроенные прототипы).

Я нахожу это ограничение встроенных модулей (не могу добавить к ним членов) один из недостатков Python, делая его несовместимым с другими определяемыми пользователем типами ... В общем, я действительно люблю этот язык, и я действительно не знаю, t понять, почему это ограничение ДЕЙСТВИТЕЛЬНО необходимо.

+8

[Zen of Python] (http://www.python.org/dev/peps/pep-0020/) - «Явный лучше, чем неявный». В принципе, это, вероятно, не будет очевидным для других людей, работающих над вашим кодом, что вы переопределили такую ​​основную особенность языка, поэтому лучше быть явным при выполнении подобных действий. – SethMMorton

+1

Нет, вы не можете переопределить какой-либо встроенный литеральный синтаксис; литералы существуют для удобства, отсутствует литеральный синтаксис для пользовательских типов * вообще *. Это добавляет ясности; вы теперь всегда * знаете *, какой тип выражает буквенный синтаксис. –

+1

Как замечание, я нахожу это ограничение функции python, а не недостатком. Я могу легко прочитать чужой код и не беспокоиться о том, что (например) '{}' не создаст 'dict'. – SethMMorton

ответ

16

Это добросовестный выбор из Python.

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

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

Литеральный синтаксис является особенно важным моментом. Если вы не можете гарантировать, что [1, 2, 3] - это список, что вы можете гарантировать? Если люди могут изменить эти поведения, это будет иметь такое глобальное воздействие, чтобы разрушить стабильность большого количества кода. Существует причина goto, и глобальные переменные обескуражены.


Существует один конкретный работник, который я я любят, хотя. Когда вы видите r"hello", это выглядит как расширенная буквальная форма.

Так почему бы не r[1, 2, 3]?

class ListPrefixer: 
    def __init__(self, typ): 
     self.typ = typ 

    def __getitem__(self, args): 
     return self.typ(args) 

class MyList(list): 
    def each(self, func): 
     return MyList(func(x) for x in self) 

e = ListPrefixer(MyList) 

e[1, 2, 3, 4].each(lambda x: x**2) 
#>>> [1, 4, 9, 16] 

Наконец, если вы действительно хотите сделать глубокий AST хаки, проверить MacroPy.

+2

+1 для «Если вы не можете гарантировать, что' [1, 2, 3] '- это список, что вы можете гарантировать? И в отношении вашего примера: вы орехи! :) –

+0

Отличное объяснение, почему это «плохо», и отличный предлагаемый обходной путь! – SethMMorton

+0

Это отличный ответ. Вы разъяснили мне кое-что. То, что вы сказали о предположениях, сделанных библиотеками, имеет смысл. Полюбите свой пример (!), Хотя это обходной путь только для конкретного случая о списках ... Спасибо за ссылку для MacroPy, я обязательно посмотрю. – rboy

Смежные вопросы