program tip

파이썬은 왜 '마법의 방법'을 사용합니까?

radiobox 2020. 8. 24. 08:00
반응형

파이썬은 왜 '마법의 방법'을 사용합니까?


나는 최근에 파이썬을 가지고 놀았는데, 조금 이상하게 발견되는 한 가지는 '마법의 방법'을 광범위하게 사용하는 것입니다. 예를 들어 길이를 사용할 수 있도록하고 객체가 메소드를 구현 한 def __len__(self)다음 언제 호출되는지 당신은 씁니다 len(obj).

객체가 단순히 len(self)메서드 를 정의하지 않고 객체 의 구성원으로 직접 호출 하는 이유가 궁금합니다 obj.len(). 파이썬이 그렇게하는 데에는 타당한 이유가있을 것 같지만, 초보자로서 나는 아직 그들이 무엇인지 알아 내지 못했습니다.


AFAIK len는이 점에서 특별하며 역사적 뿌리를 가지고 있습니다.

다음 은 FAQ 의 인용문 입니다 .

파이썬은 왜 일부 기능 (예 : list.index ())에 메서드를 사용하고 다른 기능 (예 : len (list))에 대해 사용합니까?

주된 이유는 역사입니다. 함수는 유형 그룹에 대해 일반적이고 메소드가 전혀없는 객체 (예 : 튜플)에서도 작동하도록 의도 된 작업에 사용되었습니다. 또한 Python의 기능적 기능 (map (), apply () 등)을 사용할 때 객체의 비정질 컬렉션에 쉽게 적용 할 수있는 함수를 갖는 것이 편리합니다.

실제로 len (), max (), min ()을 내장 함수로 구현하는 것은 실제로 각 유형의 메소드로 구현하는 것보다 코드가 적습니다. 개별 사례에 대해 고민 할 수 있지만 Python의 일부이며 지금 그러한 근본적인 변경을하기에는 너무 늦었습니다. 대규모 코드 손상을 방지하려면 함수를 유지해야합니다.

다른 "마법의 방법"(실제로 Python 민속학에서 특수 방법 이라고 함 )은 많은 의미가 있으며 유사한 기능이 다른 언어에 존재합니다. 특수 구문을 사용할 때 암시 적으로 호출되는 코드에 주로 사용됩니다.

예를 들면 :

  • 오버로드 된 연산자 (C ++ 및 기타에 있음)
  • 생성자 / 소멸자
  • 속성에 액세스하기위한 후크
  • 메타 프로그래밍을위한 도구

등등...


Zen of Python에서 :

모호함에도 불구하고 추측하려는 유혹을 거부하십시오.
이를 수행하는 분명한 방법은 하나, 바람직하게는 하나만 있어야합니다.

사용자 지정 방법으로, 개발자는 다른 방법 이름처럼 자유롭게 선택할 것 - 이것은 이유 중 하나입니다 getLength(), length(), getlength()또는 무엇이든지 있습니다. Python은 공통 함수를 len()사용할 수 있도록 엄격한 이름 지정을 적용 합니다.

많은 유형의 객체에 공통적 인 모든 작업은 __nonzero__, __len__또는 같은 매직 메서드에 배치됩니다 __repr__. 하지만 대부분 선택 사항입니다.

연산자 오버로딩은 매직 메서드 (예 :)로도 수행 __le__되므로 다른 일반적인 작업에도 사용하는 것이 좋습니다.


파이썬은 "마법의 방법" 이라는 단어를 사용하는데 , 그 방법은 실제로 당신의 프로그램에 마법을 수행하기 때문입니다. Python의 매직 메서드를 사용하는 가장 큰 장점 중 하나는 객체가 내장 유형처럼 동작하도록 만드는 간단한 방법을 제공한다는 것입니다. 즉, 기본 연산자를 수행하는 추악하고 직관에 반하는 비표준 방식을 피할 수 있습니다.

다음 예를 고려하십시오.

dict1 = {1 : "ABC"}
dict2 = {2 : "EFG"}

dict1 + dict2
Traceback (most recent call last):
  File "python", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'

사전 유형이 추가를 지원하지 않기 때문에 오류가 발생합니다. 이제 사전 클래스를 확장하고 "__add__" 매직 메서드를 추가해 보겠습니다 .

class AddableDict(dict):

    def __add__(self, otherObj):
        self.update(otherObj)
        return AddableDict(self)


dict1 = AddableDict({1 : "ABC"})
dict2 = AddableDict({2 : "EFG"})

print (dict1 + dict2)

이제 다음과 같은 출력을 제공합니다.

{1: 'ABC', 2: 'EFG'}

따라서이 방법을 추가하면 갑자기 마술이 일어나고 이전에 받았던 오류가 사라졌습니다.

나는 그것이 당신에게 일을 분명히하기를 바랍니다. 자세한 내용은 다음을 참조하십시오.

A Guide to Python's Magic Methods (Rafe Kettler, 2012)


Some of these functions do more than a single method would be able to implement (without abstract methods on a superclass). For instance bool() acts kind of like this:

def bool(obj):
    if hasattr(obj, '__nonzero__'):
        return bool(obj.__nonzero__())
    elif hasattr(obj, '__len__'):
        if obj.__len__():
            return True
        else:
            return False
    return True

You can also be 100% sure that bool() will always return True or False; if you relied on a method you couldn't be entirely sure what you'd get back.

Some other functions that have relatively complicated implementations (more complicated than the underlying magic methods are likely to be) are iter() and cmp(), and all the attribute methods (getattr, setattr and delattr). Things like int also access magic methods when doing coercion (you can implement __int__), but do double duty as types. len(obj) is actually the one case where I don't believe it's ever different from obj.__len__().


They are not really "magic names". It's just the interface an object has to implement to provide a given service. In this sense, they are not more magic than any predefined interface definition you have to reimplement.


While the reason is mostly historic, there are some peculiarities in Python's len that make the use of a function instead of a method appropriate.

Some operations in Python are implemented as methods, for example list.index and dict.append, while others are implemented as callables and magic methods, for example str and iter and reversed. The two groups differ enough so the different approach is justified:

  1. They are common.
  2. str, int and friends are types. It makes more sense to call the constructor.
  3. The implementation differs from the function call. For example, iter might call __getitem__ if __iter__ isn't available, and supports additional arguments that don't fit in a method call. For the same reason it.next() has been changed to next(it) in recent versions of Python - it makes more sense.
  4. Some of these are close relatives of operators. There's syntax for calling __iter__ and __next__ - it's called the for loop. For consistency, a function is better. And it makes it better for certain optimisations.
  5. Some of the functions are simply way too similar to the rest in some way - repr acts like str does. Having str(x) versus x.repr() would be confusing.
  6. Some of them rarely use the actual implementation method, for example isinstance.
  7. Some of them are actual operators, getattr(x, 'a') is another way of doing x.a and getattr shares many of the aforementioned qualities.

I personally call the first group method-like and the second group operator-like. It's not a very good distinction, but I hope it helps somehow.

Having said this, len doesn't exactly fit in the second group. It's more close to the operations in the first one, with the only difference that it's way more common than almost any of them. But the only thing that it does is calling __len__, and it's very close to L.index. However, there are some differences. For example, __len__ might be called for the implementation of other features, such as bool, if the method was called len you might break bool(x) with custom len method that does completely different thing.

In short, you have a set of very common features that classes might implement that might be accessed through an operator, through a special function (that usually does more than the implementation, as an operator would), during object construction, and all of them share some common traits. All the rest is a method. And len is somewhat of an exception to that rule.


There is not a lot to add to the above two posts, but all the "magic" functions are not really magic at all. They are part of the __ builtins__ module which is implicitly/automatically imported when the interpreter starts. I.e.:

from __builtins__ import *

happens every time before your program starts.

I always thought it would be more correct if Python only did this for the interactive shell, and required scripts to import the various parts from builtins they needed. Also probably different __ main__ handling would be nice in shells vs interactive. Anyway, check out all the functions, and see what it is like without them:

dir (__builtins__)
...
del __builtins__

참고URL : https://stackoverflow.com/questions/2657627/why-does-python-use-magic-methods

반응형