2013-02-13 5 views
12

__builtin__ 모듈의 많은 "반복자"함수는 실제로 문서가 "함수"라고 말하는 경우에도 유형으로 구현됩니다. 예를 들어 enumerate을 가져 가세요. 설명서는 다음과 동등하다고 말합니다.파이썬 내장 파이썬 함수는 실제로 어떤 타입입니까?

def enumerate(sequence, start=0): 
    n = start 
    for elem in sequence: 
     yield n, elem 
     n += 1 

정확히 어떤 방식으로 구현했는지는 분명합니다. 그러나 이전 정의에서 다음 테스트를 실행하여 이것을 얻었습니다.

>>> x = enumerate(range(10)) 
>>> x 
<generator object enumerate at 0x01ED9F08> 

내가 예상 한 바가 있습니다. 나는 그것이 오히려 문서 보여줍니다 표준 형태보다

class enumerate: 
    def __init__(self, sequence, start=0): 
     # .... 

    def __iter__(self): 
     # ... 

로 정의되어 있는지 추론이에서

>>> x = enumerate(range(10)) 
>>> x 
<enumerate object at 0x01EE9EE0> 

다음 __builtin__ 버전을 사용하는 경우 그러나,이 얻을. 이제이 방법이 어떻게 작동하는지 이해하고 표준 인 양식과 어떻게 동등한 지, 제가 알고 싶은 것은 이런 식으로하는 이유입니다. 이 방법이 더 효율적입니까? 그것은 C에서 구현되는 이러한 함수와 관련이 있습니까 (내가 있는지는 모르겠지만 그렇다고 생각합니다)?

차이점이 중요한 경우를 대비하여 Python 2.7.2를 사용하고 있습니다.

미리 감사드립니다.

+1

당신에게 문제가 있습니까? 함수와 클래스는 호출 가능한 객체 일뿐입니다 ... – JBernardo

+0

@JBernardo 거의 모든 상황에서 문제가되지는 않습니다 (그럴 때는 아마도 해킹 된 해킹을 수정해야합니다). 그러나 그것은 여전히 ​​흥미 롭습니다. – delnan

+4

아니요, 물론 아닙니다. 그것의 다만 학문적 인 질문. 발전기를 구현하는 것이 쉽기 때문에 그들의 구현이면에있는 이론적 근거를 알고 싶습니다. 그리고 어쩌면 그것은 내 자신의 발전기를 위해 이렇게해야 하는가?라는 질문에 대한 약간의 통찰력을 줄 것입니다. –

답변

10

예, 일반적으로 C에서 내장 함수가 구현된다는 사실과 관련이 있습니다. C 코드는 enumerate의 경우와 같이 일반 함수 대신 새로운 유형을 도입합니다. C로 작성하면 더 세밀하게 제어 할 수 있으며 종종 성능 향상을 위해 을 사용합니다. 실제적인 단점은 없기 때문에 자연 선택입니다.

가 동등한를 작성하는 고려 : C에서

def enumerate(sequence, start=0): 
    n = start 
    for elem in sequence: 
     yield n, elem 
     n += 1 

, 발전기의 즉 새로운 인스턴스, 당신은 실제 바이트 코드를 포함하는 코드 객체를 생성해야합니다. 이것은 불가능하지는 않지만 단순히 파이썬 C-API를 호출하는 __iter____next__을 구현하는 새로운 유형과 다른 유형을 갖는 다른 장점을 작성하는 것보다 쉽지는 않습니다.

따라서 enumeratereversed의 경우 더 나은 성능을 제공하기 때문에 간단하게 유지 관리 할 수 ​​있습니다.

다른 장점은 다음과 같습니다 : 당신은 유형 (. 예컨대 chain.from_iterable)에 대한 방법을 추가 할 수 있습니다

  • . 이것은 함수로도 할 수 있지만 먼저 정의한 다음 수동으로 속성을 설정해야합니다. 속성은 너무 깔끔하지 않습니다.
  • iterables에서 isinstance 수 있습니다. 이렇게하면 일부 최적화가 허용 될 수 있습니다 (예 : isinstance(iterable, itertools.repeat)을 알고있는 경우) 어떤 값이 산출 될지 알기 때문에 코드를 최적화 할 수 있습니다.

편집 : C에서

, 발전기의 즉 새로운 인스턴스, 당신은 실제 바이트 코드를 포함하는 코드 객체를 생성한다 : 그냥 내가 무슨 뜻인지 명확히한다. PyGen_Type 인스턴스를 생성 Objects/genobject.c 유일한 기능을 살펴보면

누구의 서명입니다 PyGen_New입니다 :

PyObject * 
PyGen_New(PyFrameObject *f) 

이제 Objects/frameobject.c보고 우리가이 PyFrameObject 당신 해야 전화 PyFrame_New을 만들 볼 수 있습니다, 이 서명이있는 :

PyFrameObject * 
PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, 
      PyObject *locals) 

당신이 그것을 볼 수 있듯이 PyCodeObject 인스턴스가 필요합니다. PyCodeObject s는 파이썬 인터프리터가 내부적으로 바이트 코드를 나타내는 방식입니다 (예 : PyCodeObject이 함수의 바이트 코드를 나타낼 수 있음). 예, C에서 PyGen_Type 인스턴스를 만들려면 수동으로 바이트 코드을 만들어야합니다. 이 같은 firstlineno, 분명히 파이썬 소스에서 얻은되지 않은 다른 C 코드에서 할 의미 filename 등의 인수를 포함하는 방법

PyCodeObject * 
PyCode_New(int argcount, int kwonlyargcount, 
      int nlocals, int stacksize, int flags, 
      PyObject *code, PyObject *consts, PyObject *names, 
      PyObject *varnames, PyObject *freevars, PyObject *cellvars, 
      PyObject *filename, PyObject *name, int firstlineno, 
      PyObject *lnotab) 

참고 : PyCode_New 이후 PyCodeObject들이 서명이 있습니다. 분명히 C 언어로 만들 수는 있지만 단순한 새로운 유형을 작성하는 것보다 적은 문자를 필요로하지는 않을 것이라고 확신합니다.

+0

해야합니까? 많은 수의 함수가 C로 작성되어 있습니다. C에서 생성자를 에뮬레이션 할 수 없다는 것이 정말로 의연합니다. – delnan

+4

C로 작성된 함수가 새로운 유형이어야하는 이유는 무엇입니까? – martineau

+0

@martineau 아마도 너무 많이 일반화했습니다. 내가 말하고자하는 것은 C에서 새로운 생성기를 생성한다는 것은 기능에 대한 바이트 코드를 수동으로 생성하는 것을 의미합니다.이 함수는 잔인하며 너무 "편하지"않습니다. '__iter__'과'__next__' 메쏘드로 새로운 타입을 작성하는 것은 매우 쉽고 더 많은 이점을 제공합니다. – Bakuriu

2

예, 그들은 C로 구현됩니다. 이터레이터는 tp_iternext 슬롯을 가진 새로운 유형을 만들어 반복기를 정의하는 반복자 (PEP 234) 용 C API를 사용합니다.

생성기 함수 구문 (yield)에 의해 생성 된 함수는 특수 생성기 개체를 반환하는 '마법'함수입니다. 이는 수동으로 생성 할 수없는 types.GeneratorType의 인스턴스입니다. C API를 사용하는 다른 라이브러리가 자체 반복자 유형을 정의하면 GeneratorType의 인스턴스가 아니지만 C API 반복자 프로토콜을 계속 구현합니다.

따라서 enumerate 유형은 GeneratorType과 다른 별개의 유형이며 다른 유형 (isinstance 등)과 같이 사용할 수 있습니다 (허용하지 않아도 됨).

Bakuriu의 대답과는 달리

, enumerate는 발생하지 않습니다, 그래서 바이트 코드/프레임가 없습니다.

$ grep -i 'frame\|gen' Objects/enumobject.c 
    PyObject_GenericGetAttr,  /* tp_getattro */ 
    PyType_GenericAlloc,   /* tp_alloc */ 
    PyObject_GenericGetAttr,  /* tp_getattro */ 
    PyType_GenericAlloc,   /* tp_alloc */ 

대신, 새 enumobject을 만드는 방법은 누구의 서명이 기능은 PyEnum_Type 구조체의 tp_new 슬롯 (내에 배치되어

static PyObject * 
enum_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 

프레임을 사용하지 않는 기능 enum_new, 함께 유형 PyTypeObject).여기에는 tp_iternext 슬롯이 enum_next 함수에 의해 점유되어 있고,이 함수는 열거되는 반복자의 다음 항목을 가져 오는 간단한 C 코드를 포함하고 PyObject (튜플)를 반환한다는 것을 알 수 있습니다.

이동 후 PyEnum_Type은 내장형 모듈 (Python/bltinmodule.c)에 enumerate이라는 이름으로 지정되어 공개적으로 액세스 할 수 있도록 배치됩니다.

바이트 코드가 필요하지 않습니다. 순수 C. 순수 Python이나 generatortype 구현보다 훨씬 효율적입니다.

+0

** ** 열거 형은 "바이트 코드"또는 프레임 객체를 요구합니다. 나는'GeneratorType'의 새로운 인스턴스를 생성하기 위해서는 그것을 필요로하고,'GeneratorType' 인스턴스를 반환하는 함수로 구현 되었다면 * 열거 할 것이라고 말했다. – Bakuriu

+1

@Bakuriu 그리고 나는 너에게 그런 것을 말하고있다. 답은 C로 생성자를 정의하는 데만 사용됩니다. 그러나 아무도 C에서 사용자 지정 반복자 형식을 정의하지 않습니다. – forivall

+0

C에서 열거 형을 함수로 바꾸려면 PyCFunction_New *를 사용하여 사용자 지정을 반환합니다. 예를 들어, PyEnum_Type을 대상으로합니다. 오리 타이핑을위한 만세 : 우리는 그것이 'GeneratorType'의 인스턴스가 아니라는 것을 신경 쓸 필요가 없다. – forivall

1

enumerate 호출은 반복자를 반환해야합니다. 반복자는 특정 API가있는 객체입니다. 특정 API를 사용하여 클래스를 구현하는 가장 쉬운 방법은 일반적으로 클래스로 구현하는 것입니다.

"class"대신 "type"이 나오는 이유는 Python 2에서 내장 클래스가 "types"로 불려 졌기 때문에 나머지 Python은 Python 2.2 이전에 유형과 클래스를 모두 가지고 있기 때문입니다. Python 2.3에서는 클래스와 유형이 통합되었습니다.

>>> enumerate 
<class 'enumerate'> 

이 귀하의 질문에 그들은 그들은 C로 구현되고 함께 할 거의가 "왜 대신 일부 기능 내장 명령 유형이다"고 명확를 만든다 : 그리고 파이썬 3에 따라서는 클래스가 말한다 타입/클래스를 구현하는 것이 가장 좋은 방법이기 때문입니다. 그것은 쉽습니다. 우리가 대신 로 질문을 해석하는 경우

지금 (매우 다른 문제이다), 그 대답은 자연스럽게 다른 "왜 형/클래스 대신에 발전기가 enumerate입니다." 파이썬 함수에서 반복자를 생성하기위한 파이썬 단축키는 생성자입니다. 그들은 C에서 사용하기위한 것이 아닙니다. 또한 클래스 밖의 메소드보다 생성기를 생성하는 데 덜 유용합니다. 예를 들어 객체 컨텍스트를 전달해야하는 클래스 메소드에서 반복자 객체를 생성하려는 경우, 그러나 기능이 필요하지 않습니다. 따라서 코드를 덜 "스캐 폴딩"하는 것이 대부분 이점입니다.

+0

나는 python3/python2의 차이점이 OP 질문과 관련된 * 아무 것도 * 볼 수 없다는 것을 알지 못합니다. 또한 "기능을 구현하는 가장 좋은 방법이기 때문에 유형/클래스"라는 것이 명백합니다. 그렇지 않으면 파이썬 개발자가 시간 낭비없이 아무런 어려움없이 어려운 일을하는 것을 의미합니다. OP 질문은보다 구체적입니다. – Bakuriu

+0

@Bakuriu : 요점은 Python 2가 "유형"이라고 부르는 것은 사람들에게 C 언어로 구현되는 것과 관련이 있다고 생각하게 만들었습니다. 이는 두 개의 다른 답변에서 분명합니다. 이것은 ** 잘못된 **입니다. C로 구현되는 것과 아무런 관련이 없습니다. 이것은 더 이상 유형이 아니라 클래스 인 Python 3에서 분명합니다. –

+0

@ 바큐 리 질문을 명확히했습니다. –

관련 문제