문제

Ruby는 숫자 클래스 및 기타 핵심 유형에 메소드를 추가하여 다음과 같은 효과를 얻을 수 있습니다.

1.should_equal(1)

그러나 파이썬이 이것을 할 수없는 것 같습니다. 이것이 사실입니까? 그렇다면 왜? 사실과 관련이 있습니까? 유형 수정할 수 없습니까?

업데이트 : 원숭이 패치의 다양한 정의에 대해 이야기하기보다는 위의 예에 집중하고 싶습니다. 나는 이미 당신 중 일부가 대답 한 것처럼 할 수 없다고 결론을 내 렸습니다. 그러나 왜 그렇게 할 수 없는지에 대한 자세한 설명을 원하고 아마도 Python에서 사용 가능한 경우 어떤 기능이이를 허용 할 수 있는지에 대한 자세한 설명을 원합니다.

여러분 중 일부에 대답하기 위해 : 이유 i ~할 것 같다 이를 원하시는 것은 단순히 미학/가독성입니다.

 item.price.should_equal(19.99)

이것은 영어와 비슷하게 읽히고 테스트 된 값이며 예상 값이 무엇인지 명확하게 나타냅니다.

should_equal(item.price, 19.99)

이 개념은 무엇입니다 RSPEC 그리고 다른 루비 프레임 워크는 기반을두고 있습니다.

도움이 되었습니까?

해결책

여기서 원숭이 패치가 정확히 무엇을 의미합니까? 거기 있습니다 약간 다른 정의.

"런타임에 클래스의 메소드를 변경할 수 있습니까?"라는 의미가 있다면 대답은 강조 적입니다.

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에서 구현되며 구현을보다 쉽고 효율적으로 만들기 위해 희생 된 특정 추상화가 있습니다.

나는 정말 명확하지 않다 어쨌든 내장 숫자 클래스의 동작을 변경하고 싶을 것입니다. 당신이 그들의 행동을 바꾸어야한다면, 그들을 서브 클래스!

다른 팁

아니 당신은 할 수 없습니다. Python에서 C 확장 모듈 (빌드 딘 포함)에 정의 된 모든 데이터 (클래스, 메소드, 기능 등)는 불변입니다. 이는 동일한 프로세스에서 C 모듈이 여러 통역사간에 공유되기 때문에 동일한 프로세스에서 관련 통역사에 영향을 미칩니다. (동일한 과정의 여러 통역사가 C API, 그리고 거기가있었습니다 약간의 노력 파이썬 수준에서 사용할 수 있도록합니다.)

그러나 Python 코드로 정의 된 클래스는 해당 통역사에 로컬이기 때문에 원숭이가 방해받을 수 있습니다.

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

재미있게 보내십시오;)

당신은 이것을 할 수 있지만 약간의 해킹이 필요합니다. 다행히도, 현재 "Forbidden Fruit"라는 모듈이 있으며 내장 유형의 패치 방법을 매우 간단하게 제공합니다. 당신은 그것을 찾을 수 있습니다

http://clarete.github.io/forbiddenfruit/?goback=.gde_50788_member_228887816

또는

https://pypi.python.org/pypi/forbiddenfruit/0.1.0

원래 질문 예제를 사용하면 "Dish_equal"기능을 작성한 후

from forbiddenfruit import curse
curse(int, "should_equal", should_equal)

그리고 당신은 가기 좋다! 패치 된 메소드를 제거하기위한 "역"함수도 있습니다.

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'

당신은 확실히 ~할 수 있었다 파이썬의 사용자 정의 유형은 기본적으로 변이하기 때문에 서브 클래스를 만들어 설명하는 효과를 달성하십시오.

>>> class MyInt(int):
...   def frobnicate(self):
...     print 'frobnicating %r' % self
... 
>>> five = MyInt(5)
>>> five.frobnicate()
frobnicating 5
>>> five + 8
13

만들 필요가 없습니다 MyInt 서브 클래스 공개; 인스턴스를 구성하는 함수 또는 메소드에서 직접 인라인을 직접 정의 할 수 있습니다.

관용구에 유창한 파이썬 프로그래머가 이런 종류의 서브 클래스를 올바른 일을 고려하는 몇 가지 상황이 있습니다. 예를 들어, os.stat() 반환 a tuple 당신의 예제에서 참조하는 가독성 문제를 해결하기 위해 이름 지정된 멤버를 추가하는 서브 클래스.

>>> import os
>>> st = os.stat('.')
>>> st
(16877, 34996226, 65024L, 69, 1000, 1000, 4096, 1223697425, 1223699268, 1223699268)
>>> st[6]
4096
>>> st.st_size
4096

즉, 당신이주는 특정한 예에서 나는 그 하위 클래싱을 믿지 않습니다. float 안에 item.price (또는 다른 곳)는 할 일이 될 가능성이 높습니다. 나 ~할 수 있다 누군가가 추가하기로 결정하는 사람을 쉽게 상상해보십시오 price_should_equal() 방법 item 그것이 기본 사용 사례라면; 더 일반적인 것을 찾고 있다면 아마도 이름이 지정된 인수를 사용하여 의도 된 의미를 명확하게 만드는 것이 더 합리적 일 수 있습니다.

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

또는 그 줄을 따라 무언가. 약간의 장점이지만 의심 할 여지없이 개선 될 수 있습니다. 루비 스타일의 원숭이 패치에 대한 이러한 접근 방식에 대한 가능한 이점은 should_equal() 뿐만 아니라 모든 유형에 대한 비교를 쉽게 수행 할 수 있습니다. int 또는 float. 그러나 아마도 나는 당신이 제공 한 특정 예의 세부 사항에 너무 사로 잡히고 있습니다.

파이썬에서 코어 유형을 패치 할 수 없습니다. 그러나 파이프를 사용하여 인간이 읽을 수있는 코드를 작성할 수 있습니다.

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)

구현의 예는 다음과 같습니다 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)

당신이 정말로 정말로 진짜 파이썬에서 원숭이 패치를 원하시면 "import foo"기술로 (정렬) 해킹을 할 수 있습니다.

TelnetConnection과 같은 클래스가 있고 확장하려면 별도의 파일로 서브 클래스를 클래스하고 TelnetConnectionExtended와 같은 호출하십시오.

그런 다음 코드 상단에서 일반적으로 말할 수 있습니다.

import TelnetConnection

그것을 변경하십시오.

import TelnetConnectionExtended as TelnetConnection

TelnetConnection을 참조하는 코드의 어느 곳에서나 실제로 TelnetConnectionExtended를 참조합니다.

안타깝게도 이것은 해당 클래스에 액세스 할 수 있다고 가정하고 "AS"는 특정 파일 내에서만 작동합니다 (글로벌 이름이 아닙니다). 그러나 때때로 유용하다는 것을 알았습니다.

아니요. 그러나 당신은 이것을 정확하게 염두에두고 userdict userstring 및 userlist를 가지고 있습니다.

Google의 경우 다른 유형에 대한 예제를 찾을 수 있지만 이는 내장됩니다.

일반적으로 원숭이 패치는 루비보다 파이썬에서 덜 사용됩니다.

아니요, 파이썬에서는 할 수 없습니다. 나는 그것이 좋은 것으로 생각합니다.

무엇을 하는가 should_equal 하다? 부울 귀환입니까? True 또는 False? 이 경우 철자가 있습니다.

item.price == 19.99

취향에 대한 설명은 없지만 일반적인 파이썬 개발자는 버전보다 읽기 쉬운 것이라고 말할 수 없습니다.

하다 should_equal 대신 일종의 유효성 검사기를 설정 하시겠습니까? (유효성 검사기가 하나의 값으로 제한되는 이유는 무엇입니까? 왜 값을 설정하고 그 후에 업데이트하지 않습니까?) 유효성 검사기를 원한다면 특정 정수 또는 모든 것을 수정할 것을 제안하기 때문에 어쨌든 작동하지 않을 수 있습니다. 정수. (필요한 유효성 검사기 18.99 동일하게 19.99 항상 실패합니다.) 대신, 당신은 다음과 같이 철자를 할 수 있습니다.

item.price_should_equal(19.99)

아니면 이거:

item.should_equal('price', 19.99)

항목 클래스 또는 슈퍼 클래스에서 적절한 방법을 정의하십시오.

당신이 정말로 쓰고 싶었던 것은 다음과 같습니다.

assert item.price == 19.99

(물론 평등을 위해 수레를 비교하거나 가격에 부유물을 사용하는 것 나쁜 생각, 당신은 글을 쓸 것입니다 assert item.price == Decimal(19.99) 또는 가격에 사용했던 숫자 클래스.)

같은 테스트 프레임 워크를 사용할 수도 있습니다 py.test 테스트에서 실패한 어셈블리에 대한 자세한 정보를 얻으려면.

아니요, 슬프게도 런타임에 C에서 구현 된 유형을 확장 할 수 없습니다.

INT 서브 클래스를 할 수 있지만 사소하지는 않지만 재정의해야 할 수도 있습니다. __new__.

구문 문제도 있습니다.

1.somemethod()  # invalid

하지만

(1).__eq__(1)  # valid

다음은 내가 .hould_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에도 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top