Если в 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, и даже существующие экземпляры этого класса могут использовать этот введенный метод.