왜 파이썬 메소드에 "self"인수가 명시 적으로 필요합니까?


197

파이썬에서 클래스에 메소드를 정의 할 때 다음과 같이 보입니다 :

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

그러나 C #과 같은 다른 언어에서는 메소드 프로토 타입에서 인수로 선언하지 않고 메소드가 "this"키워드를 사용하여 바인딩 된 오브젝트에 대한 참조가 있습니다.

이것은 파이썬에서 의도적 인 언어 디자인 결정입니까, 아니면 "자기"를 인수로 전달해야하는 구현 세부 사항이 있습니까?


15
난 당신이 또한 당신이 명시 적으로 작성해야하는 이유 알고에 관심이있을 것입니다 내기 self- 액세스 구성원 stackoverflow.com/questions/910020/...
표트르 Dobrogost을

1
그러나 그것은 다소 보일러 판처럼 보인다
Raghuveer

혼동되기 비트 만의 가치를 이해 stackoverflow.com/a/31367197/1815624
CrandellWS

답변:


91

저는 Peters의 Zen of Python을 인용하고 싶습니다. "명시적인 것이 묵시적인 것보다 낫다."

Java 및 C ++에서 this.추론을 불가능하게하는 변수 이름이있는 경우를 제외하고 ' '를 추론 할 수 있습니다. 따라서 때로는 필요하지만 때로는 필요하지 않습니다.

파이썬은 규칙에 기반하기보다는 이와 같은 것을 명시 적으로 선택합니다.

또한 암시되거나 가정 된 것이 없으므로 구현의 일부가 노출됩니다. self.__class__, self.__dict__및 기타 "내부"구조는 명백한 방법으로 사용할 수 있습니다.


53
잊어 버릴 때 덜 비밀스러운 오류 메시지가 표시되는 것이 좋습니다.
Martin Beckett

9
그러나 메소드를 호출 할 때 객체 변수를 전달할 필요가 없습니다. 명시 성 규칙을 위반하지 않습니까? 이 선을 유지하려면 object.method (object, param1, param2)와 같아야합니다. 어떻게 든 일관성이없는 것 같습니다 ...
Vedmant

10
"명시적인 것이 묵시적인 것보다 낫다"-파이썬의 "스타일"이 묵시적인 것을 중심으로 구축 된 것이 아닌가? 예 : 암시 적 데이터 유형, 암시 적 함수 경계 ({}) 없음, 암시 적 변수 범위 ... 모듈의 전역 변수를 함수에서 사용할 수있는 경우 ... 왜 동일한 논리 / 추론을 클래스에 적용해서는 안됩니까? 들여 쓰기에 의해 결정된대로 단순화 된 규칙이 "높은 수준에서 선언 된 것은 낮은 수준에서 사용 가능"하지 않습니까?
Simon

13
넌센스 감지
Vahid Amiri

10
그것을 직면하자, 그것은 단지 나쁘다. 이에 대한 변명이 없습니다. 추악한 유물 일 뿐이지 만 괜찮습니다.
Toskan

63

방법과 기능의 차이를 최소화하는 것입니다. 메타 클래스에서 메소드를 쉽게 생성하거나 런타임시 기존 클래스에 메소드를 추가 할 수 있습니다.

예 :

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

또한 (내가 아는 한) 파이썬 런타임을 쉽게 구현할 수 있습니다.


10
방법과 함수의 차이를 최소화하기 위해 +1입니다. 이 답변에 동의해야합니다
사용자

이것은 또한 guido의 많은 관련 설명의 뿌리에 있습니다.
Marcin

1
또한 파이썬에서는 c.bar ()를 수행 할 때 먼저 인스턴스의 속성을 확인한 다음 클래스 속성을 확인 합니다. 따라서 클래스에 데이터 또는 함수 (객체)를 '첨부'할 수 있으며 인스턴스에서 액세스 할 것으로 예상됩니다 (예 : dir (instance)는 방법입니다). c instance를 "만들었을 때"만이 아닙니다. 매우 역동적입니다.
Nishant

10
나는 그것을 실제로 사지 않습니다. 부모 클래스가 필요한 경우에도 실행시 유추 할 수 있습니다. 그리고 인스턴스 메소드와 클래스 함수 사이의 동등성은 어리석은 일입니다. 루비는 그것들없이 잘 작동합니다.
zachaysan

2
JavaScript를 사용하면 런타임에 객체에 메소드를 추가 할 수 self있으며 함수 선언에 필요하지 않습니다 (JavaScript에는 매우 까다로운 this바인딩 의미론 이 있기 때문에 유리 하우스에서 돌을 던질 수 있습니다 )
Jonathan Benn

55

나는 사람이 읽어야하는 것이 좋습니다 귀도 반 로섬 (Guido van Rossum)의 블로그 -이 주제에 대한 명시 적 자기 유지하는 이유는 .

메소드 정의가 데코레이션 될 때 자동으로 'self'매개 변수를 제공할지 여부를 알 수 없습니다. 데코레이터는 함수를 정적 메소드 ( 'self'가없는) 또는 클래스 메소드 ( 인스턴스 대신 클래스를 참조하는 재미있는 종류의 자체가 있거나 완전히 다른 무언가를 할 수 있습니다 (순수한 파이썬에서 '@classmethod'또는 '@staticmethod'를 구현하는 데코레이터를 작성하는 것은 쉽지 않습니다). 메소드가 암시적인 'self'인수로 정의되는지 여부를 데코레이터가 무엇을하는지 알지 못하면 방법이 없습니다.

특수한 '@classmethod'및 '@staticmethod'와 같은 핵을 거부합니다.


16

파이썬은 "self"를 사용하도록 강요하지 않습니다. 원하는 이름을 지정할 수 있습니다. 메소드 정의 헤더의 첫 번째 인수는 객체에 대한 참조임을 기억하면됩니다.


그러나 관례에 따라 유형이 관련된 인스턴스 또는 'cls'에 대해 '자체'여야합니다 (mmmm 메타 클래스)
pobk

1
그것은 모든 방법에서 자신을 첫 번째 매개 변수로 사용하도록 강요합니다. 다른 언어는 이것과 잘 작동합니다.
Vedmant

내가 맞아? 항상 첫 번째 매개 변수는 객체에 대한 참조입니다.
Mohammad Mahdi Kouchak 야지 디

예를 들어 @MMKY 아니요 @staticmethod.
Mark

1
"메소드 정의의 첫 번째 인수를 기억해야합니다 ..." "self"라는 단어를 "kwyjibo"로 바꾸는 실험을 해보았지만 여전히 효과가있었습니다. 따라서 종종 설명 하듯이 중요한 것은 "자기"라는 단어가 아니라 그 공간을 차지하는 모든 것의 위치 (?)
RBV

7

또한 이렇게하면 다음과 같은 작업을 수행 할 수 있습니다. (요컨대, 호출 Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5)하면 12가 반환되지만 가장 열중 한 방법으로 호출 됩니다.

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

물론 이것은 Java 및 C #과 같은 언어에서는 상상하기 어렵습니다. 자체 참조를 명시 적으로 작성하면 해당 자체 참조로 오브젝트를 자유롭게 참조 할 수 있습니다. 또한 런타임에 클래스를 사용하는 이러한 방법은 더 정적 인 언어에서는 수행하기가 더 어렵습니다. 반드시 좋은지 나쁜지는 아닙니다. 그것은 명백한 자아가이 모든 광기의 존재를 허용한다는 것입니다.

또한, 이것을 상상해보십시오 : 우리는 메소드의 동작 (프로파일 링 또는 미친 흑 마법)을 사용자 정의하고 싶습니다. 이를 통해 우리는 생각을 Method할 수 있습니다.

여기 있습니다 :

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

그리고 지금 : InnocentClass().magic_method()예상대로 행동합니다. 이 메서드는 innocent_self매개 변수 to InnocentClassmagic_selfMagicMethod 인스턴스에 바인딩됩니다 . 허드? 2 개의 키워드가 this1있고 this2Java 및 C #과 같은 언어로되어 있습니다. 이와 같은 마술은 프레임 워크가 훨씬 더 장황한 것들을 할 수있게합니다.

다시, 나는이 물건의 윤리에 대해 언급하고 싶지 않다. 나는 명백한 자기 참조 없이는하기 어려운 것들을 보여주고 싶었습니다.


4
첫 번째 예제를 고려할 때 Java에서 똑같이 할 수 있습니다. 내부 클래스 OuterClass.this는 외부 클래스에서 '자기'를 얻기 위해 호출해야 하지만 여전히 this자체에 대한 참조로 사용할 수 있습니다 . 파이썬에서 여기서하는 것과 매우 비슷합니다. 나를 위해 이것을 상상하는 것이 더 힘들지 않았습니다. 어쩌면 문제의 언어 능력에 달려 있습니까?
klaar

그러나 익명 클래스 내부에 정의 된 익명 클래스의 메소드 안에있을 때 여전히 스코프를 참조 할 수 있습니까?이 클래스는 익명의 인터페이스 구현 Something내부에 정의되어 있습니다 Something. 파이썬에서는 물론 모든 범위를 참조 할 수 있습니다.
vlad-ardelean

당신은 옳습니다. Java에서는 명시 적 클래스 이름을 호출하여 외부 클래스를 참조하고 접두사로 사용할 수 this있습니다. Java에서는 암시 적 참조가 불가능합니다.
klaar

그래도 이것이 효과가 있는지 궁금합니다. 각 범위 (각 방법마다)에는 this결과 를 참조하는 로컬 변수가 있습니다. 예를 들어 Object self1 = this;(Object를 사용하거나 덜 일반적인 것). 당신이 더 높은 범위에서 변수에 액세스 할 수있는 경우 그런 다음, 당신이 액세스를 가질 수있다 self1, self2... selfn. 나는 이것들이 최종 또는 무언가로 선언되어야한다고 생각하지만, 효과가있을 수 있습니다.
vlad-ardelean

3

"파이썬 (Zen of Python)"외에 실제 이유는 함수가 파이썬에서 일류 시민이라는 것입니다.

본질적으로 그것들을 객체로 만듭니다. 이제 근본적인 문제는 함수가 객체 인 경우 객체 지향 패러다임에서 메시지 자체가 객체 일 때 객체에 메시지를 보내는 방법은 무엇입니까?

이 역설을 줄이기 위해 닭고기 달걀 문제처럼 보이는 유일한 방법은 실행 컨텍스트를 메소드에 전달하거나 감지하는 것입니다. 그러나 파이썬은 중첩 함수를 가질 수 있기 때문에 실행 컨텍스트가 내부 함수에 대해 변경되므로 그렇게 할 수 없습니다.

이것은 가능한 유일한 해결책은 명시 적으로 'self'(실행 컨텍스트)를 전달하는 것입니다.

그래서 Zen이 훨씬 나중에 구현 문제라고 생각합니다.


안녕하세요, 저는 (Java 배경에서) python을 처음 접했고 "메시지 자체가 객체 일 때 어떻게 메시지를 객체로 보내겠습니까?"라고 말한 내용을 따르지 않았습니다. 그게 왜 문제입니까? 정교하게 설명 할 수 있습니까?
Qiulang

1
@Qiulang Aah는 객체에 대한 객체 지향 프로그래밍 호출 메소드에서 페이로드가 있거나없는 함수 (메소드에 대한 매개 변수)를 사용하여 메시지를 객체에 발송하는 것과 같습니다. 내부적으로 메소드는 클래스 / 오브젝트와 연관된 코드 블록으로 표시되며 호출 된 오브젝트를 통해 사용 가능한 암시 적 환경을 사용합니다. 그러나 메소드가 객체 인 경우 클래스 / 객체와 관련이없는 독립적 인 상태로 존재할 수 있습니다.이 메소드를 호출하면 어떤 환경에 대해 문제가 발생합니까?
pankajdoharey

따라서 환경을 제공하는 메커니즘이 있어야합니다. self는 실행 시점에서 현재 환경을 의미하지만 다른 환경도 제공 할 수 있습니다.
pankajdoharey

2

PEP 227과 관련이 있다고 생각합니다.

클래스 범위의 이름은 액세스 할 수 없습니다. 가장 안쪽에있는 함수 범위에서 이름이 확인됩니다. 중첩 된 범위의 체인에서 클래스 정의가 발생하면 해결 프로세스는 클래스 정의를 건너 뜁니다. 이 규칙은 클래스 속성과 로컬 변수 액세스 간의 이상한 상호 작용을 방지합니다. 클래스 정의에서 이름 바인딩 조작이 발생하면 결과 클래스 오브젝트에 속성이 작성됩니다. 메소드 또는 메소드 내에 중첩 된 함수에서이 변수에 액세스하려면 자체 또는 클래스 이름을 통해 속성 참조를 사용해야합니다.


1

파이썬 에서 스스로 설명했듯이 Demystified

obj.meth (args)와 같은 것은 Class.meth (obj, args)가됩니다. 수신 프로세스는 명시 적이 지 않지만 호출 프로세스는 자동입니다. 이것이 클래스에서 함수의 첫 번째 매개 변수가 객체 자체 여야하는 이유입니다.

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from origin"""
        return (self.x**2 + self.y**2) ** 0.5

호출 :

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init ()는 세 개의 매개 변수를 정의하지만 방금 두 개 (6과 8)를 전달했습니다. 마찬가지로 distance ()에는 하나의 인수가 필요하지만 0 개의 인수가 전달되었습니다.

파이썬 이이 인수 번호 불일치 에 대해 불평하지 않는 이유는 무엇 입니까?

일반적으로 인수가있는 메소드를 호출 할 때 해당 클래스 함수는 메소드의 오브젝트를 첫 번째 인수 앞에 배치하여 호출됩니다. 따라서 obj.meth (args)와 같은 것은 Class.meth (obj, args)가됩니다. 수신 프로세스는 명시 적이 지 않지만 호출 프로세스는 자동입니다.

이것이 클래스에서 함수의 첫 번째 매개 변수가 객체 자체 여야하는 이유입니다. 이 매개 변수를 자체로 작성하는 것은 단지 규칙 입니다. 키워드가 아니며 파이썬에서 특별한 의미가 없습니다. 우리는 (이와 같은) 다른 이름을 사용할 수 있지만 강력히 추천하지는 않습니다. self가 아닌 다른 이름을 사용하면 대부분의 개발자가 눈살을 찌푸리고 코드의 가독성을 떨어 뜨립니다 ( "가독성").
...
첫 번째 예인 self.x는 인스턴스 속성이고 x는 로컬 변수입니다. 그것들은 동일하지 않으며 다른 네임 스페이스에 있습니다.

자아는 여기에있다

많은 사람들이 C ++ 및 Java에서와 같이 파이썬에서 자체 키워드를 제안했습니다 . 이렇게하면 메소드의 공식 매개 변수 목록에서 명시 적 자체를 중복 사용하지 않아도됩니다. 이 아이디어는 유망한 것처럼 보이지만 일어나지 않을 것입니다. 적어도 가까운 장래에는 그렇지 않습니다. 주된 이유는 이전 버전과의 호환성 입니다. 다음은 파이썬 제작자가 작성한 블로그에서 왜 명시 적 자아가 머물러야 하는지를 설명합니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.