可在Python的核心类型,您的猴子打补丁的方法呢?可在Python的核心类型,您的猴子打补丁的方法

2019-05-08 19:58发布

红宝石可以添加方法Number类及其他核心类型得到这样的效果:

1.should_equal(1)

但它似乎是Python不能做到这一点。 这是真的? 如果是这样,为什么呢? 是否有事可做的事实, 类型不能修改?

更新:而不是谈论猴子修补不同的定义,我想只专注于上面的例子。 我已经得出结论,不能作为少数来完成的,你已经回答了。 但我想,为什么不能做了更详细的解释,也许什么功能,如果在Python可用,将允许这一点。

要回答你们几个,我可能会想这样做的原因很简单,美观/可读性。

 item.price.should_equal(19.99)

这读起来更像是英文清楚地表明这是测试值,这是预期价值,应该:

should_equal(item.price, 19.99)

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

Answer 1:

究竟你的猴子补丁意思的是什么? 有几个稍有不同的定义 。

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

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语言实现,并有以使实现更简单,更高效的牺牲一定的抽象。

我不是你为什么会想无论如何改变内置数字类的行为真的很清楚。 如果你需要改变他们的行为,继承他们!



Answer 2:

你不能。 在Python,在C扩展模块(包括内建)中定义的所有数据(类,方法,函数,等等)是不可变的。 这是因为C模块在同一进程中多个解释器之间共享,这样的Monkeypatching它们也会影响无关口译在相同的处理。 (在同一进程中多个解释是可能通过C API ,并出现了一些努力朝着使它们在Python的水平使用。)

然而,在Python代码中定义的类可monkeypatched因为他们是局部的解释。



Answer 3:

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

玩得开心 ;)



Answer 4:

你可以这样做,但它需要黑客的一点点。 幸运的是,有一个现在被称为“禁果”,让您得以修补内置类型的方法非常简单的电源模块。 你可以找到它

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)

你是好去! 还有一个“反”功能删除修补方法。



Answer 5:

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

这就是说,在你给具体的例子,我不相信,子类floatitem.price (或其他地方)将很有可能被认为是Python的事情。 我可以很容易想象有人决定一个添加price_should_equal()方法来item如果这是主用例; 如果一个人在寻找更多的东西一般,也许它可能会更有意义使用命名参数进行具体含义更加清晰,如

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

或类似的规定。 这是一个有点冗长,但毫无疑问,它可以改进。 可能的优点超过红宝石式猴修补这种方法是should_equal()可以很容易地执行其上的任何类型的比较,而不是仅仅intfloat 。 但是,也许我过于在你碰巧提供的特定例子的细节赶上了。



Answer 6:

你不能在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)


Answer 7:

下面是实现的一个例子item.price.should_equal ,虽然我会在实际的程序中使用,而不是浮动十进制:

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)


Answer 8:

如果你真的真的真的想要做Python中的猴子补丁,你可以做的“进口FOO,酒吧”技术一(sortof)黑客。

如果你有一个类,如TelnetConnection的,和你想扩展它,继承它在一个单独的文件,并调用它像TelnetConnectionExtended。

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

import TelnetConnection

更改为:

import TelnetConnectionExtended as TelnetConnection

然后在你的代码到处您引用TelnetConnection的实际上是引用TelnetConnectionExtended。

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



Answer 9:

没有,但你的UserDict UserString和用户列表,用的正是这种考虑做出。

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

一般来说猴子修补是在Python比红宝石较少采用。



Answer 10:

没有,可悲的是,你不能扩展在运行C实现的类型。

你也可以继承INT,虽然这是不平凡的,你可能要重写__new__

你也有一个语法问题:

1.somemethod()  # invalid

然而

(1).__eq__(1)  # valid


Answer 11:

不,你不能这样做在Python。 我认为这是一件好事。



Answer 12:

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

item.price == 19.99

有没有核算的味道,但没有正规的Python开发会说这是不是你的版本的可读性。

是否should_equal而不是设置某种验证的? (为什么一个验证局限于一个价值?为什么不只是设置的值,而不是之后更新了吗?)如果您想验证程序,这可能永远反正工作,因为你提出修改任何一个特殊的整数或全部整数。 (需要一个验证18.99等于19.99总是会失败。)相反,你可能会给这样的:

item.price_should_equal(19.99)

或这个:

item.should_equal('price', 19.99)

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



Answer 13:

看来你真的想写的是:

assert item.price == 19.99

(当然比较花车平等,或者使用float型的价格,是一个坏主意 ,所以你会写assert item.price == Decimal(19.99)或任何数字类你正在使用的价格。)

你也可以使用一个测试框架喜欢py.test获得对失败的详细信息在您的测试断言。



Answer 14:

下面是如何实现.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

这也是PyPI上的下pyspecs。



文章来源: Can you monkey patch methods on core types in Python?