Это естественно. Там анимация просто для примера через setInterval, поэтому много раз нажали — много таймеров запустили :) Здесь цель была просто показать что анимация есть и работает…
Функцию реализовать можно, но для чего? Приборы призваны отображать значения которые вы им даете. Зачем вам их копировать (если вы их и так знаете)? :)
Кроме того, если даже такая функция с какой-то целью и понадобилась — должно ли это быть частью самих приборов? Ведь ничто не мешает это реализовать «сбоку», равно как и можно реализовать сохранение в картинку (снэпшот) и т.д.
Я просто ищу ответы на такие вопросы, ведь, как правило, я добавляю в библиотеку лишь тот функционал, который имеет для пользователей практическое значние, при этом стараюсь не раздувать ее код.
Например, пользователи обратились и просят добавить возможность анимировать не стрелку, а циферблат, т.к. они делают компас на приборной панели и это для них критично. И сделать это они ну никак не могут без изменения библиотеки. С другой стороны, раньше с библиотекой поставлялся цифровой шрифт, но пользователи жаловались, что это не вмещается в концепцию минималистичности кода, поэтому его теперь нет, но есть возможность прикручивать любой шрифт по желанию сбоку. Поэтому у меня вопрос — а нужно ли реализовывать копирование в самих приборах? Не теоретически «это бвыло бы круто», а практически — «действительно ли это нужно»?
Ведь можно приделать стотыщпиццот функций, в том числе функию нанесения ответного ядерного удара Марса по Венере, но зачем?
Прошу прощения, но, как вы сказали, как бы "«canvas» уже намекает на это" — это все-таки динамически создаваемая растровая графика (bitmap) а не текст. Возможно вы хотели бы SVG, но
это таки canvas gauges. :)
Итак, мы выяснили, что для решения такой ситуации придется парсить исходник (что и делают эти мои сторонние библиотеки). Такой код выполниться только один раз при запуске (что нам собственно и нужно, и поэтому не критично, т.е. можем с этим мириться).
Кстати, если кто-то может ткнуть меня мордой в то, как обойти парсинг исходников в такой ситуации — я буду просто благодарен и наконец-то стану спать спокойно.
Усложняем модель.
Вводим второй декоратор, который делает в какой-то мере то же самое (опять парсит, опять единожды при запуске, но делает что-то уже другое, но нам также полезное). Но так как второй декоратор будет декорировать не метод класса (а по написанному коду — это метод, объявленный внутри класса — так код написан!), а декоратор этого метода, то выходит, что будем парсить далеко не тот кусок кода, который ожидаем. (напоминаю — это не мое, чужое, но мне с ним жить)
Вот тут и поможет моя библиотека. Она позволяет получить get_real_function(). Но это будет доступно только если оба декоратора «зарегистрированы» перед определением класса. При этом декоратор, который используется сверху, переопределяем, подсовывая нативному реальную функцию. И все работает. И овцы целы, и волки сыты. Кстати можно переопределить таким способом оба и тогда можно вообще забить на порядок их следования при написании кода классов.
А теперь уж, я надеюсь, не составляет труда представить ситуацию, когда может понадобится проверить, а не задекорирован ли какой-то метод каким-то декоратором. А раз может, то и мы можем такую возможность в нашей библиотеке предоставить и несколько других подобных.
Я специально не хотел описывать этот кейс (он очень специфичный), а подать статью немного в другом ракурсе, как бы «вот библиотека, вот это она умеет». Но не фартануло… :)
Дорогой КО, зачем вы мне это пишете?
Я говорю именно о том, что подобный подход мне не нравится в Python.
Ваш пример:
>>> def m():
... pass
...
>>> def m(self):
... pass
...
>>> class A(object):
... f = m
...
>>> class B(object):
... f = m
...
>>> A.f.im_class
<class '__main__.A'>
>>> B.f.im_class
<class '__main__.B'>
>>> m.im_class
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'im_class'
Теперь объявляем класс с таким задекорированым методом:
>>> def d( fn):
... print fn.im_class
... def w(*a, **k):
... return fn(*a, **k)
... return w
...
>>> class X(object):
... @d
... def f(self):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in X
File "<stdin>", line 2, in d
AttributeError: 'function' object has no attribute 'im_class'
Все вы даже определить класс не можете! Чтобы это все таки работало — будете парсить код. Я не вижу другого решения. Вы видите?
И еще раз — я не пишу библиотеку «для парсинга исходников». Я просто использую библиотеку, у которой это внутри.
Вообще-то, если по-честному, я немного лукавлю, говоря, что библиотеки написаны не очень хорошо. Просто сам Python, на мой взгляд устроен не совсем хорошо. Сам факт того, что при декорировании, метод переданый в декоратор становиться unbound функцией и зарефлектить, к какому классу принадлежит данный метод, не представляется возможным без таких вещей, как f.__code__.co_filename, f.__code__.co_firstlineno, и с последующим парсингом исходника с целью найти какому классу принадлежит данный метод. Если же декораторов было несколько, то вы вообще теряете связь с самим исходным методом! Вам опять может стать непонятным для чего это нужно, но ведь, в целом, рефлексии позволяют создавать некоторые абстракции, что может быть полезным при проектировании некоторых библиотек общего назначения. Все было бы гораздо проще, если бы на уровне языка рефлексии, да и реализация декораторов, были бы немного продуманнее.
Я имею на это сказать. Почему по вашему он не ожидает, что он не задекорирован? Как он об этом знает? Вед декоратор только знает о функции, которую он декорирует. Я вед именно эту задачу и решаю — позволить себе знать не только вниз но и вверх. Если он об этом знает, значит он использует мою библиотеку? :)
Правы. Вас это смущает? В конце концов я не агитирую использовать данную библиотеку именно в таком контексте. Можно на это, в конце-концов, посмотреть под другим углом. К примеру, вы ищете решение такой задачи — узнать какие методы в заданном классе задекорированы заданным декоратором. Данная библиотека позволяет задачу решить без парсинга исходного кода. Сам язык такого инструментария не дает. Это что, что-то совершенно бесполезное?
Вот жеж вы зануды.
Хорошо, пусть будет такой пример. Начнем с того, что есть сторонняя библиотека «один» поставляющая некий «мега-функционал», который «включается» декоратором «one». Есть вторая, такая же важначя и нужная и поставляет декоратор «two».
Есть некий наш класс:
class MyClass(object):
def my_method(self):
pass
Если вы применяете декораторы из двух библиотек по одному на ваших методах — все работает. Так работает:
class MyClass(object):
@one
def my_method(self):
pass
и так работает:
class MyClass(object):
@two
def my_method(self):
pass
А так не работает:
class MyClass(object):
@one
@two
def my_method(self):
pass
и так не работает:
class MyClass(object):
@two
@one
def my_method(self):
pass
Не работает потому, что сами библиотеки внутри возможно написаны не очень хорошо (такое бывает?) и каждая из них делает нечно, для чего ожидает что переданный в ее декоратор метод — это метод класса, патается каким-то образом отрефлектить этот метод, и что-то там сделать — не важно. Поэтому в таком варианте не работает по той причине что верхний декоратор декорирует уже задекорированый метод, а не исходный. В качестве решения я и воспользовался таким подходом — регистрирую оба декоратора в реестре и один из них переопределяю, передавая реальную функцию, когда она не доступна. Остальной функционал по рефлексии получился в качестве дополнительного инструментария, что иногда полезно, как минимум в дебаге. Сторонние же библиотеки остались нетронутыми, и не будет проблем с их обновлением. Может это и не очень правильно, как вы говорите, но зато удобно.
Может быть оно и не самое хорошее но наиболее удобное, так как позволяет не править сторонюю библиотеку и иметь впоследствии проблемы с ее обновлением. К сожалению описание деталей тянет на отдельную статью… Ваше право считать так как вы считаете.
UPD: исправлено
Кроме того, если даже такая функция с какой-то целью и понадобилась — должно ли это быть частью самих приборов? Ведь ничто не мешает это реализовать «сбоку», равно как и можно реализовать сохранение в картинку (снэпшот) и т.д.
Я просто ищу ответы на такие вопросы, ведь, как правило, я добавляю в библиотеку лишь тот функционал, который имеет для пользователей практическое значние, при этом стараюсь не раздувать ее код.
Например, пользователи обратились и просят добавить возможность анимировать не стрелку, а циферблат, т.к. они делают компас на приборной панели и это для них критично. И сделать это они ну никак не могут без изменения библиотеки. С другой стороны, раньше с библиотекой поставлялся цифровой шрифт, но пользователи жаловались, что это не вмещается в концепцию минималистичности кода, поэтому его теперь нет, но есть возможность прикручивать любой шрифт по желанию сбоку. Поэтому у меня вопрос — а нужно ли реализовывать копирование в самих приборах? Не теоретически «это бвыло бы круто», а практически — «действительно ли это нужно»?
Ведь можно приделать стотыщпиццот функций, в том числе функию нанесения ответного ядерного удара Марса по Венере, но зачем?
это таки canvas gauges. :)
Итак, мы выяснили, что для решения такой ситуации придется парсить исходник (что и делают эти мои сторонние библиотеки). Такой код выполниться только один раз при запуске (что нам собственно и нужно, и поэтому не критично, т.е. можем с этим мириться).
Кстати, если кто-то может ткнуть меня мордой в то, как обойти парсинг исходников в такой ситуации — я буду просто благодарен и наконец-то стану спать спокойно.
Усложняем модель.
Вводим второй декоратор, который делает в какой-то мере то же самое (опять парсит, опять единожды при запуске, но делает что-то уже другое, но нам также полезное). Но так как второй декоратор будет декорировать не метод класса (а по написанному коду — это метод, объявленный внутри класса — так код написан!), а декоратор этого метода, то выходит, что будем парсить далеко не тот кусок кода, который ожидаем. (напоминаю — это не мое, чужое, но мне с ним жить)
Вот тут и поможет моя библиотека. Она позволяет получить get_real_function(). Но это будет доступно только если оба декоратора «зарегистрированы» перед определением класса. При этом декоратор, который используется сверху, переопределяем, подсовывая нативному реальную функцию. И все работает. И овцы целы, и волки сыты. Кстати можно переопределить таким способом оба и тогда можно вообще забить на порядок их следования при написании кода классов.
А теперь уж, я надеюсь, не составляет труда представить ситуацию, когда может понадобится проверить, а не задекорирован ли какой-то метод каким-то декоратором. А раз может, то и мы можем такую возможность в нашей библиотеке предоставить и несколько других подобных.
Я специально не хотел описывать этот кейс (он очень специфичный), а подать статью немного в другом ракурсе, как бы «вот библиотека, вот это она умеет». Но не фартануло… :)
Я говорю именно о том, что подобный подход мне не нравится в Python.
Ваш пример:
Теперь объявляем класс с таким задекорированым методом:
Все вы даже определить класс не можете! Чтобы это все таки работало — будете парсить код. Я не вижу другого решения. Вы видите?
И еще раз — я не пишу библиотеку «для парсинга исходников». Я просто использую библиотеку, у которой это внутри.
Ну вам уже не придется так напрягаться — можете воспользоваться моей :)
> Просто исправьте их и судя по вашему рассказу не будет вообще никаких проблем ни с чем.
Да и так нет проблем ни с чем
Хорошо, пусть будет такой пример. Начнем с того, что есть сторонняя библиотека «один» поставляющая некий «мега-функционал», который «включается» декоратором «one». Есть вторая, такая же важначя и нужная и поставляет декоратор «two».
Есть некий наш класс:
Если вы применяете декораторы из двух библиотек по одному на ваших методах — все работает. Так работает:
и так работает:
А так не работает:
и так не работает:
Не работает потому, что сами библиотеки внутри возможно написаны не очень хорошо (такое бывает?) и каждая из них делает нечно, для чего ожидает что переданный в ее декоратор метод — это метод класса, патается каким-то образом отрефлектить этот метод, и что-то там сделать — не важно. Поэтому в таком варианте не работает по той причине что верхний декоратор декорирует уже задекорированый метод, а не исходный. В качестве решения я и воспользовался таким подходом — регистрирую оба декоратора в реестре и один из них переопределяю, передавая реальную функцию, когда она не доступна. Остальной функционал по рефлексии получился в качестве дополнительного инструментария, что иногда полезно, как минимум в дебаге. Сторонние же библиотеки остались нетронутыми, и не будет проблем с их обновлением. Может это и не очень правильно, как вы говорите, но зато удобно.