54

Rubyは、Numberクラスやその他のコアタイプにメソッドを追加して、次のような効果を得ることができます。

1.should_equal(1)

しかし、Pythonではこれができないようです。これは本当ですか?もしそうなら、なぜですか?タイプを変更できないという事実と関係がありますか?

更新:モンキーパッチのさまざまな定義について話すのではなく、上記の例に焦点を当てたいと思います。あなた方の何人かが答えたので、私はすでにそれをすることができないと結論を下しました。しかし、なぜそれができないのか、そしておそらくPythonで利用可能な場合、どの機能がこれを可能にするのかについて、より詳細な説明が必要です。

あなたの何人かに答えるために:私がこれをしたいと思うかもしれない理由は単に美学/読みやすさです。

 item.price.should_equal(19.99)

これは英語に似ており、次のように、テストされた値と期待値を明確に示しています。

should_equal(item.price, 19.99)

この概念は、Rspecおよび他のいくつかのRubyフレームワークが基づいているものです。

4

14 に答える 14

76

いいえ、あなたがすることはできません。Python では、C 拡張モジュール (組み込みを含む) で定義されたすべてのデータ (クラス、メソッド、関数など) は不変です。これは、C モジュールが同じプロセス内の複数のインタープリター間で共有されているためです。そのため、それらにモンキーパッチを適用すると、同じプロセス内の無関係なインタープリターにも影響が及びます。(同じプロセス内の複数のインタープリターはC APIを介して可能であり、Python レベルで使用できるようにするための努力が行われています。)

ただし、Python コードで定義されたクラスは、そのインタープリターに対してローカルであるため、モンキー パッチが適用される場合があります。

于 2008-10-10T20:01:31.990 に答える
45

ここでのモンキーパッチとは正確にはどういう意味ですか? 若干異なる定義がいくつかあります。

「実行時にクラスのメソッドを変更できますか?」という意味であれば、答えは断固として「はい」です。

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

intただし、やなどの特定の組み込みクラスではこれを行うことはできませんfloat。これらのクラスのメソッドは C で実装されており、実装をより簡単かつ効率的にするために特定の抽象化が犠牲になっています。

とにかく、組み込みの数値クラスの動作を変更したい理由がよくわかりません。それらの動作を変更する必要がある場合は、それらをサブクラス化してください!!

于 2008-10-10T19:15:42.207 に答える
31
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")

楽しむ ;)

于 2010-10-26T15:37:39.423 に答える
14

他のユーザーが指摘しているように、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(または他の場所で)サブクラス化することがPythonicのことと見なされる可能性は非常に高いとは思いません。それが主なユースケースである場合、誰かがメソッドを追加することを決定することを容易に想像できます。より一般的なものを探している場合は、次のように、意図した意味をより明確にするために名前付き引数を使用する方が理にかなっているかもしれません。price_should_equal()item

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

またはそれらの線に沿った何か。少し冗長ですが、改善できることは間違いありません。Ruby スタイルのモンキー パッチに対するこのようなアプローチの考えられる利点は、orshould_equal()だけでなく、任意の型に対して簡単に比較を実行できることです。しかし、あなたがたまたま提供してくれた特定の例の詳細にとらわれすぎているのかもしれません。intfloat

于 2008-10-11T04:50:58.223 に答える
8

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)
于 2011-05-13T06:35:19.300 に答える
4

本当に本当に本当にPythonでモンキーパッチを実行したい場合は、「foo asbarをインポートする」手法を使用して(一種の)ハックを実行できます。

TelnetConnectionなどのクラスがあり、それを拡張する場合は、別のファイルにサブクラス化して、TelnetConnectionExtendedなどの名前を付けます。

次に、コードの上部で、通常は次のように言います。

import TelnetConnection

次のように変更します。

import TelnetConnectionExtended as TelnetConnection

そして、TelnetConnectionを参照するコード内のすべての場所で、実際にはTelnetConnectionExtendedが参照されます。

残念ながら、これはあなたがそのクラスにアクセスできることを前提としており、「as」はその特定のファイル内でのみ機能します(グローバルな名前変更ではありません)が、時々役立つことがわかりました。

于 2008-10-10T19:09:16.760 に答える
4

を実装する例を次に示しますitem.price.should_equalが、実際のプログラムでは float の代わりに Decimal を使用します。

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)
于 2009-05-08T02:36:45.263 に答える
1

いいえ。ただし、これを念頭に置いて作成されたUserDictUserStringとUserListがあります。

グーグルすると他のタイプの例が見つかりますが、これは組み込みです。

一般に、モンキーパッチはRubyよりもPythonで使用されません。

于 2008-10-10T19:13:08.173 に答える
1

あなたが本当に書きたかったのは次のようです。

assert item.price == 19.99

(もちろん、浮動小数点数を等しいかどうか比較したり、価格に浮動小数点数を使用したりするのは悪い考えassert item.price == Decimal(19.99)です。そのため、価格に使用していた数値クラスを記述します。)

py.testのようなテスト フレームワークを使用して、テストで失敗したアサートに関する詳細情報を取得することもできます。

于 2011-06-05T20:22:55.243 に答える
1

何をしshould_equalますか?ブール値の戻り値TrueですかFalse? その場合、次のように綴られます。

item.price == 19.99

好みの説明はありませんが、通常の python 開発者は、それがあなたのバージョンよりも読みにくいとは言いません。

should_equal代わりに、ある種のバリデーターを設定しますか? (バリデーターが 1 つの値に限定されるのはなぜですか? 値を設定するだけで、その後は更新しないのはなぜですか?) バリデーターが必要な場合は、特定の整数またはすべてを変更することを提案しているため、とにかくこれは機能しません。整数。18.99( equalを必要とするバリデーター19.99は常に失敗します。)代わりに、次のように綴ることもできます。

item.price_should_equal(19.99)

またはこれ:

item.should_equal('price', 19.99)

アイテムのクラスまたはスーパークラスで適切なメソッドを定義します。

于 2009-05-06T15:15:39.107 に答える
0

いいえ、Pythonではそれを行うことはできません。いいことだと思います。

于 2008-10-10T18:57:11.403 に答える
0

いいえ、残念ながら、実行時に C で実装された型を拡張することはできません。

int をサブクラス化できますが、それは自明ではありませんが、オーバーライドする必要がある場合があります__new__

構文の問題もあります。

1.somemethod()  # invalid

でも

(1).__eq__(1)  # valid
于 2012-12-20T11:19:35.087 に答える
-1

.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

また、pyspecs の下の PyPI にもあります。

于 2012-06-05T03:38:45.557 に答える