15

在我听到的许多关于 Ruby 的讨论中,人们表达了他们对该语言的保留意见,猴子修补问题成为他们主要关注的问题之一。

然而,我很少听到在 Python 上下文中提出的相同论点,尽管 Python 语言也允许这样做。

为什么会有这种区别?

Python 是否包含不同类型的保护措施以最大程度地降低此功能的风险?

4

8 回答 8

21

这是一种在 Python 中较少实践的技术,部分原因是 Python 中的“核心”类(那些用 C 实现的)并不是真正可修改的。另一方面,在 Ruby 中,由于它在内部实现的方式(不是更好,只是不同),几乎任何东西都可以动态修改。

从哲学上讲,它在 Python 社区中往往不受欢迎,而在 Ruby 世界中则明显不那么重要。我不知道您为什么断言它更具争议性(您可以链接到权威参考吗?)-我的经验是猴子修补是一种公认​​的技术,如果用户应该意识到可能的后果的话。

于 2009-04-04T18:22:54.143 回答
16

语言可能允许这样做,但两个社区都不会容忍这种做法。Monkeypatching 在这两种语言中都不会被宽恕,但是你在 Ruby 中更经常听到它,因为它使用的开放类的形式使得对一个类进行猴子补丁非常非常容易,正因为如此,它在 Ruby 社区中更容易接受,但是还是皱着眉头。Monkeypatching 在 Python 中并不那么普遍或容易,这就是为什么您不会在该社区中听到相同的反对意见的原因。Python 没有做任何 Ruby 没有做的事情来阻止这种做法。

您在 Ruby 中更频繁地听到/阅读它的原因是在 Ruby 中:

class MyClass
  def foo
    puts "foo"
  end
end
class MyClass
  def bar
    puts "bar"
  end
end

将为您提供一个包含两个方法的类foobar,而这在 Python 中:

class MyClass:
    def foo(self):
        print "foo"
class MyClass:
    def bar(self):
        print "bar"

会给你留下一个只包含方法bar的类,因为类的重新定义完全破坏了之前的定义。要在 Python 中进行monkeypatch,你实际上必须这样写:

class MyClass:
    def foo(self):
        print "foo"
def bar(self):
    print "bar"
MyClass.bar = bar

这比 Ruby 版本更难。仅这一点就使得 Ruby 代码比 Python 代码更容易进行猴子补丁。

于 2009-04-04T18:03:18.777 回答
16

作为一个尝过 Ruby(并且喜欢它)的 Python 程序员,我认为这与 Python 开始流行的时候有点讽刺。

C 和 Java 程序员会“抨击”Python,声称它不是一种真正的语言,其类型的动态特性会很危险,并允许人们创建“坏”代码。随着 Python 变得越来越流行,其快速开发时间的优势变得明显,更不用说不那么冗长的语法了:

// Java
Person p = new Person();
# Python
p = Person()

我们开始看到一些更动态的特性出现在更高版本的 Java 中。自动装箱和拆箱使处理原语变得不那么麻烦,而泛型允许我们编写一次代码并将其应用于多种类型。

我很高兴地看到 Ruby 的关键灵活特性之一——猴子补丁,被 Python 人群吹捧为危险。今年开始向学生教授 Ruby,我认为能够“修复”现有课程的实现,即使是系统的一部分,也是非常强大的。

当然,你可能搞砸了,你的程序可能会崩溃。我也可以很容易地在 C 中进行段错误。Java 应用程序可能会死于火焰般的死亡。

事实上,我认为 Monkey Patching 是动态和元编程的下一步。有趣,因为它自 Smalltalk 以来一直存在。

于 2009-04-04T23:48:08.933 回答
13

“Python 是否包含不同类型的保护措施以最大限度地降低此功能的风险?”

是的。社区拒绝这样做。保障完全是社会性的。

于 2009-04-04T18:17:20.057 回答
3

实际上在 Python 中修改基本类型有点困难。

例如想象一下,您重新定义整数。

红宝石:

class Fixnum 
   def *(n)
      5 
   end 
end

现在 2*2 产生 5。

Python:

>>> class int(int):
    def __mul__(self, x):
        return 5


>>> 2*2
4
>>> int(2)*int(2)
5
于 2009-04-04T22:27:42.700 回答
3

在 Python 中,任何文字(""{}1.0等)都会创建标准类的实例,即使您尝试对其进行猴子补丁并在命名空间中重新定义相应的类。

它只是不会按您的意图工作:

class str():
    # define your custom string type
    ...

a = "foo"      # still a real Python string
a = str("foo") # only this uses your custom class
于 2009-04-05T02:57:28.057 回答
2

我认为猴子补丁应该只作为最后的解决方案。

通常,Python 程序员知道类或方法的行为方式。他们知道 xxx 班正在以某种方式做事。

当你对一个类或一个方法进行修补时,你正在改变它的行为。如果该类的行为不同,其他使用该类的 Python 程序员可能会感到非常惊讶。

正常的做事方式是子类化。这样,其他程序员就知道他们正在使用不同的对象。他们可以选择使用原始类或子类。

于 2009-04-06T11:18:34.757 回答
1

如果你想在 Python 中做一些猴子补丁,这相对容易,只要你不修改内置类型(int、float、str)。

class SomeClass:
    def foo(self):
        print "foo"

def tempfunc(self):
    print "bar"
SomeClass.bar = tempfunc
del tempfunc

这会将 bar 方法添加到 SomeClass ,甚至该类的现有实例也可以使用该注入方法。

于 2009-04-07T16:08:35.613 回答