你可以在Python的核心types猴子补丁方法?

Ruby可以将方法添加到Number类和其他核心types以获得如下效果:

 1.should_equal(1)

但是,似乎python不能做到这一点。 这是真的? 如果是这样,为什么? 这是否与types无法修改的事实有关?

更新:而不是谈论猴子补丁的不同定义,我想只关注上面的例子。 我已经得出结论说,这是不可能的,因为你们中的一些人已经回答了。 但是我想要更详细地解释为什么它不能完成,也许什么function,如果在Python中可用,将允许这样做。

回答你们中的一些人:我可能想要这样做的原因只是美学/可读性。

 item.price.should_equal(19.99)

阅读更像英文,并清楚地指出哪个是testing值,哪个是期望值,如下所示:

 should_equal(item.price,19.99)

这个概念是Rspec和其他一些Ruby框架的基础。

Monkey Patch在这里的意思是什么? 有几个略有不同的定义 。

如果你的意思是“你能在运行时改变一个类的方法吗?”,那么答案是肯定的:

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() 

但是你不能为特定的内置类,如intfloat 。 这些类的方法在C中实现,为了使实现更容易,更高效,牺牲了某些抽象。

我不清楚你为什么要改变内置数字类的行为。 如果你需要改变他们的行为,他们的子类!

你不能。 在Python中,C扩展模块(包括内build函数)中定义的所有数据(类,方法,函数等)都是不可变的。 这是因为C模块是由同一个进程中的多个解释器共享的,所以对它们进行monkeypatching也会影响不相关的解释器在同一个进程中。

然而,在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") 

玩的开心 ;)

你可以做到这一点,但需要一点点的黑客攻击。 幸运的是,现在有一个名为“Forbidden Fruit”的模块,可以非常简单地为内置types的方法提供补丁。 你可以find它

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) 

你很好走! 还有一个“反向”function来删除修补方法。

正如其他用户所指出的那样,Python的核心types是不可变的,

 >>> 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' 

您当然可以通过创build子类达到您描述的效果,因为Python中的用户定义types默认是可变的。

 >>> 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 

也就是说,在你给出的具体例子中,我不相信item.price (或其他地方)中的子类float很可能被认为是Pythonic的事情。 我很容易想象有人决定添加一个price_should_equal()方法到item如果这是主要的用例; 如果有人在寻找更一般的东西,那么使用命名的参数来使意图的意义更清楚,或许更有意义

 should_equal(observed=item.price, expected=19.99) 

或沿着这些线路的东西。 这有点冗长,但毫无疑问可以改进。 对于Ruby风格的猴子补丁,这种方法的一个可能的好处是, should_equal()可以很容易地在任何types上执行它的比较,而不仅仅是int或者float 。 但是,也许我已经陷入了你碰巧提供的具体例子的细节中。

你不能在Python中修补核心types。 但是,您可以使用pipe编写更易读的代码:

 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) 

下面是一个实现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中做一个猴子补丁,你可以用“import foo as bar”技术做一个(一些)破解。

如果你有一个类如TelnetConnection,并且你想扩展它,在一个单独的文件中将其子类化,并将其称为类似于TelnetConnectionExtended的东西。

然后,在您的代码的顶部,您通常会说:

 import TelnetConnection 

改变是:

 import TelnetConnectionExtended as TelnetConnection 

然后在您的代码中的任何地方,您引用TelnetConnection实际上是引用TelnetConnectionExtended。

可悲的是,这个假定你有权访问那个类,而且“as”只在这个特定的文件中运行(这不是全局重命名),但是我发现它有时是有用的。

不,可悲的是你不能在运行时扩展在C中实现的types。

你可以__new__ int类,尽pipe它不重要,你可能必须重载__new__

你也有一个语法问题:

 1.somemethod() # invalid 

然而

 (1).__eq__(1) # valid 

不,你不能用Python做到这一点。 我认为这是一件好事。

不,但是你有UserDict UserString和UserList,这正是考虑到这一点。

如果你谷歌,你会发现其他types的例子,但这是内置的。

一般来说,Python中的猴子修补比Ruby中less。

should_equal做什么的? 它是一个返回TrueFalse的布尔值? 在这种情况下,拼写如下:

 item.price == 19.99 

没有味道的会计,但没有一个普通的Python开发人员会说这比你的版本更不可读。

should_equal是否设置了某种validation器? (为什么一个validation器被限制为一个值?为什么不设置这个值,在那之后不更新呢?)如果你想要一个validation器,那么这个工作根本无法工作,因为你打算修改一个特定的整数或所有的整数。 (需要18.9919.99validation器总是会失败。)相反,你可以这样拼写:

 item.price_should_equal(19.99) 

或这个:

 item.should_equal('price', 19.99) 

并在项目的类或超类上定义适当的方法。

看来你真的想写的是:

 assert item.price == 19.99 

(当然,比较浮动平等,或使用浮动价格,是一个坏主意 ,所以你会写价值assert item.price == Decimal(19.99)或任何数字类)。

你也可以使用像py.test这样的testing框架来获取testing中失败的断言的更多信息。

以下是我如何实现.should_something …行为:

 result = calculate_result('blah') # some method defined somewhere else the(result).should.equal(42) 

要么

 the(result).should_NOT.equal(41) 

我包含一个装饰器方法,用于在独立方法的运行时扩展此行为:

 @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

它也在PyPy下的pyspecs。