Если в Ruby и Python разрешено перехватывать обезьяны, почему в Ruby это более противоречиво?

Во многих дискуссиях я слышал о Ruby, в котором люди высказывали свои оговорки в отношении языка, проблема исправления обезьян является одной из их основных проблем.

Однако я редко слышу те же аргументы, что и в контексте Python, хотя это также разрешено на языке Python.

Почему это различие?

Включает ли Python различные типы защит для минимизации рисков этой функции?

Ответы

Ответ 1

Как программист на Python, у которого был вкус Ruby (и ему нравится), я думаю, что есть несколько ироническая параллель, когда Python начинает становиться популярным.

Программисты C и Java будут bash Python, заявив, что это не настоящий язык, и что динамический характер его типов будет опасным и позволит людям создавать "плохой код". Поскольку Python стал более популярным, и преимущества его быстрого времени разработки стали очевидными, не говоря уже о менее подробном синтаксисе:

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

мы начали видеть, что в более поздних версиях Java появляются более динамические функции. Autoboxing и -unboxing делают менее трудным заниматься примитивами, а Generics позволяют нам кодировать один раз и применять его ко многим типам.

Было с некоторым развлечением, что я увидел одну из ключевых гибких функций Ruby - Monkey Patching, которая была рекламирована как опасная толпой Python. Начав преподавать Ruby студентам в этом году, я думаю, что возможность "исправлять реализацию существующего класса, даже одного, входящего в систему, очень эффективна".

Конечно, вы можете сильно испортиться, и ваша программа может потерпеть крах. Я тоже могу segfault на C довольно легко. И приложения Java могут умереть пылающей смертью.

По правде говоря, я вижу Monkey Patching как следующий шаг в динамическом и метапрограммировании. Забавно, поскольку он был вокруг с Smalltalk.

Ответ 2

Этот метод менее практичен в Python, отчасти потому, что "основные" классы в Python (те, что реализованы на C) на самом деле не изменяются. В Ruby, с другой стороны, из-за того, как он реализован внутренне (не лучше, просто по-другому), практически все может быть изменено динамически.

Философски, это то, что, как правило, недооценивается внутри сообщества Python, а тем более в мире Ruby. Я не знаю, почему вы утверждаете, что это более противоречиво (можете ли вы ссылаться на авторитетную ссылку?) - мой опыт в том, что обезьяна-паттинг является принятой техникой, если пользователь должен знать о возможных последствиях.

Ответ 3

Языки могут это разрешить, но ни одно сообщество не одобряет практику. 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

предоставит вам класс, который содержит два метода: foo и bar, тогда как это в Python:

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

оставит вас с классом, который содержит только метод bar, поскольку переопределение класса полностью перекрывает предыдущее определение. Для monkeypatch в Python вам действительно нужно написать это:

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

что сложнее, чем версия Ruby. Это само по себе делает код Ruby намного проще для monkeypatch, чем код Python.

Ответ 4

"Включает ли Python различные типы защит для минимизации рисков этой функции?"

Да. Сообщество отказывается это делать. Защита полностью социальная.

Ответ 5

На самом деле в Python немного сложнее изменить базовые типы.

Например, представьте, что вы переопределите целое число.

Ruby:

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

Ответ 6

В 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

Ответ 7

Я думаю, что исправление обезьяны должно использоваться только как последнее решение.

Обычно программисты на Python знают, как ведет себя класс или метод. Они знают, что класс xxx делает что-то определенным образом.

Когда вы обезвреживаете какой-либо класс или метод, вы меняете его поведение. Другие программисты Python, использующие этот класс, могут быть очень удивлены, если этот класс ведет себя по-другому.

Обычный способ делать вещи - это подклассы. Таким образом, другие программисты знают, что они используют другой объект. Они могут использовать исходный класс или подкласс, если они захотят.

Ответ 8

Если вы хотите сделать некоторые исправления для обезьян в Python, это относительно легко, если вы не изменяете встроенный тип (int, float, str).

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

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

Это добавит метод бара в SomeClass, и даже существующие экземпляры этого класса могут использовать этот введенный метод.