Я бы не сделал это. Мое правило: методы предоставления услуг со всем, что им нужно для выполнения своей работы, и ничего больше .
Почему?
Итак, если есть что-либо в модели представления, которая не играет роли в методе службы, не используйте ее для вызова метода.
Что именно вы подразумеваете под патчем Monkey здесь? Есть несколько несколько разных определений .
Если вы имеете в виду, «можете ли вы изменить методы класса во время выполнения?», Тогда ответ решительно да:
class Foo:
pass # dummy class
Foo.bar = lambda self: 42
x = Foo()
print x.bar()
Если вы имеете в виду: «Можете ли вы изменить методы класса во время выполнения и сделать все экземпляры этого класса после факта?» тогда и ответ да. Просто немного измените порядок:
class Foo:
pass # dummy class
x = Foo()
Foo.bar = lambda self: 42
print x.bar()
Но вы не можете сделать это для определенных встроенных классов, например int
или float
. Эти методы классов реализованы в C, и некоторые абстракции пожертвованы, чтобы сделать реализацию проще и эффективнее.
Я не совсем понимаю, почему вы хотели бы изменить поведение встроенного - В числовых классах все равно. Если вам нужно изменить свое поведение, подклассируйте их !!
Вы можете это сделать, но это требует немного взлома. К счастью, теперь есть модуль, называемый «Forbidden Fruit», который дает вам возможность исправлять методы встроенных типов очень просто. Вы можете найти его в
http://clarete.github.io/forbiddenfruit/?goback=.gde_50788_member_228887816
или
https://pypi.python.org/pypi/forbiddenfruit/0.1.0
С исходным вопросом вопроса после того, как вы напишете функцию «should_equal», вы просто сделайте
from forbiddenfruit import curse
curse(int, "should_equal", should_equal)
, и вам хорошо идти! Существует также функция «обратного» удаления исправленного метода.
Нет, к сожалению, вы не можете расширять типы, реализованные в C во время выполнения.
Вы можете подклассировать int, хотя это нетривиально, вам может потребоваться переопределить __new__
.
У вас также есть проблема с синтаксисом:
1.somemethod() # invalid
Однако
(1).__eq__(1) # valid
Вот как я могу достичь .should_something ... поведения:
result = calculate_result('blah') # some method defined somewhere else
the(result).should.equal(42)
the(result).should_NOT.equal(41)
Я включил метод декоратора для расширения этого поведения во время выполнения на стенде -alone method:
@should_expectation
def be_42(self)
self._assert(
action=lambda: self._value == 42,
report=lambda: "'{0}' should equal '5'.".format(self._value)
)
result = 42
the(result).should.be_42()
Вы должны знать немного о внутренних элементах, но он работает.
Вот источник:
https: //github.com/mdwhatcott/pyspecs
Он также находится в PyPI под pyspecs.
Если вы действительно действительно хотите сделать патч обезьяны в Python, вы можете сделать (sortof) взломать с помощью метода «import foo as bar».
Если у вас есть класс, такой как TelnetConnection, и вы хотите его расширить, подклассифицируйте его в отдельный файл и назовите его чем-то вроде TelnetConnectionExtended.
Затем в верхней части вашего кода, где вы обычно говорите:
import TelnetConnection
измените это значение:
import TelnetConnectionExtended as TelnetConnection
, а затем всюду в вашем коде, который вы ссылаетесь на TelnetConnection, действительно будет ссылаться на TelnetConnectionExtended.
К сожалению, это предполагает, что у вас есть доступ к этому классу, а «как» работает только в этом конкретном файле (это не глобальное переименование), но я нашел его полезным время от времени.
Нет, вы не можете. В Python все данные (классы, методы, функции и т. Д.), Определенные в модулях расширения C (включая встроенные), неизменяемы. Это связано с тем, что C-модули совместно используются несколькими интерпретаторами в одном и том же процессе, поэтому monkeypatching them также влияет на несвязанных интерпретаторов в одном и том же процессе.
Однако классы, определенные в коде Python, могут быть обезврежены, поскольку они локальны для этот интерпретатор.
def should_equal_def(self, value):
if self != value:
raise ValueError, "%r should equal %r" % (self, value)
class MyPatchedInt(int):
should_equal=should_equal_def
class MyPatchedStr(str):
should_equal=should_equal_def
import __builtin__
__builtin__.str = MyPatchedStr
__builtin__.int = MyPatchedInt
int(1).should_equal(1)
str("44").should_equal("44")
Удачи;)
from HisPatch import *
– Hanfei Sun
19 February 2013 в 13:44
HisPatch
приведет к выполнению этого кода и глобально i> изменить встроенные классы. Не то, чтобы я не рассматривал использование чего-то вроде этого ...
– Tobias Kienzler
8 December 2014 в 14:05
Нет, но у вас есть UserDict UserString и UserList, которые были сделаны именно с этим.
Если вы используете Google, вы найдете примеры для других типов, но это встроенные функции.
В общем, патч обезьяны менее используется в Python, чем в Ruby.
Вы не можете изменять основные типы в python. Однако вы можете использовать трубу для написания более понятного для человека кода:
from pipe import *
@Pipe
def should_equal(obj, val):
if obj==val: return True
return False
class dummy: pass
item=dummy()
item.value=19.99
print item.value | should_equal(19.99)
Кажется, что вы действительно хотели написать:
assert item.price == 19.99
(Конечно, сравнение float для равенства или использование float для цен - это плохая идея , поэтому вы напишете assert item.price == Decimal(19.99)
или какой-нибудь цифровой класс, который вы использовали для цены.)
Вы также можете использовать платформу тестирования, например py.test , чтобы получить дополнительную информацию о сбоях утверждает в ваших тестах.
Вот пример реализации item.price.should_equal
, хотя я бы использовал Decimal вместо float в реальной программе:
class Price(float):
def __init__(self, val=None):
float.__init__(self)
if val is not None:
self = val
def should_equal(self, val):
assert self == val, (self, val)
class Item(object):
def __init__(self, name, price=None):
self.name = name
self.price = Price(price)
item = Item("spam", 3.99)
item.price.should_equal(3.99)
Нет, вы не можете сделать это в Python. Я считаю, что это хорошо.
Что делает should_equal
? Это логическое возвращение True
или False
? В этом случае это написано:
item.price == 19.99
У вас нет учета вкуса, но обычный разработчик python не сказал бы, что это менее читаемо, чем ваша версия.
Установил ли should_equal
какой-то валидатор? (почему валидатор должен быть ограничен одним значением? Почему бы просто не установить значение и не обновить его после этого?) Если вы хотите валидатор, это никогда не сможет работать в любом случае, так как вы предлагаете изменить либо определенное целое число, либо все целые числа. (Валидатор, который требует 18.99
равным 19.99
, всегда терпит неудачу.) Вместо этого вы могли бы записать его следующим образом:
item.price_should_equal(19.99)
или это:
item.should_equal('price', 19.99)
и определить соответствующие методы класса или суперклассов.
Основные типы Python неизменяемы по дизайну, как указывают другие пользователи:
>>> int.frobnicate = lambda self: whatever()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'int'
Вы, конечно, могли бы добиться эффекта, который вы описываете, создавая подкласс, поскольку пользователь Определенные типы в Python по умолчанию изменяются.
>>> class MyInt(int):
... def frobnicate(self):
... print 'frobnicating %r' % self
...
>>> five = MyInt(5)
>>> five.frobnicate()
frobnicating 5
>>> five + 8
13
Нет необходимости публиковать подкласс MyInt
, либо; можно точно определить его непосредственно в функции или методе, который конструирует экземпляр.
Конечно, есть несколько ситуаций, когда программисты Python, свободно владеющие идиомой, считают, что этот класс подкласса правилен делать. Например, os.stat()
возвращает подкласс tuple
, который добавляет именованные члены, точно для того, чтобы устранить проблему с точки зрения удобочитаемости, о которой вы говорите в своем примере.
>>> import os
>>> st = os.stat('.')
>>> st
(16877, 34996226, 65024L, 69, 1000, 1000, 4096, 1223697425, 1223699268, 1223699268)
>>> st[6]
4096
>>> st.st_size
4096
Тем не менее, в конкретном пример, который вы даете, я не считаю, что подклассификация float
в item.price
(или в другом месте) с большой вероятностью будет считаться питонической задачей. I может легко представить, что кто-то решил добавить метод price_should_equal()
к item
, если это был основной случай использования; если бы кто-то искал что-то более общее, возможно, было бы разумнее использовать именованные аргументы, чтобы сделать предполагаемый смысл более ясным, как в
should_equal(observed=item.price, expected=19.99)
или что-то в этом направлении. Это немного многословно, но, без сомнения, его можно было бы улучшить. Возможным преимуществом такого подхода по сравнению с рубиновым обезьян-патчем является то, что should_equal()
может легко выполнить сравнение по любому типу, а не только int
или float
. Но, возможно, я слишком зациклен на деталях конкретного примера, который вам удалось предоставить.