Python 클래스에서 여러 생성자 메서드 오버로드 및 일반 메서드를 정의하는 방법

WBOY
풀어 주다: 2023-05-09 14:34:23
앞으로
1291명이 탐색했습니다.

    "일반 메서드"란 무엇인가요?

    다른 유형에 대해 동일한 작업을 구현하는 여러 메서드로 구성된 메서드입니다.

    예를 들어

    이제 다음과 같은 방법으로 사용자 정의 날짜 클래스(CustomDate)를 생성해야 하는 요구 사항이 있습니다. CustomDate):

    • 时间戳

    • 年、月、日(包含三个整数的元组)

    • ISO 格式的字符串

    • Datetime

    一般实现

    from datetime import date, datetime
    class CustomDate:
        def __init__(self, arg):
            if isinstance(arg, (int, float)):
                self.__date = date.fromtimestamp(arg)
            elif isinstance(arg, tuple) and len(arg) == 3 and all(map(lambda x: isinstance(x, int), arg):
                self.__date = date(*arg)
            elif isinstance(arg, str):
                self.__date = date.fromisoformat(arg)
            elif isinstance(arg, datetime):
                self.__date = datetime.date()
            else:
                raise TypeError("could not create instance from " + type(arg).__name__)
        @property
        def date():
            return self.__date
    로그인 후 복사

    注:这里暂不讨论传入的日期/时间戳合不合法,仅仅只对类型做大致判断。

    有没有更好的方式?

    我们可以将不同的构建方式拆分为多个方法,并利用 functools 中的 singledispatchmethod 装饰器来根据传入的参数类型决定调用哪个方法。

    from datetime import date, datetime
    from functools import singledispatchmethod
    class CustomDate:
        @singledispatchmethod
        def __init__(self, arg):
            raise TypeError("could not create instance from " + type(arg).__name__)
        @__init__.register(int)
        @__init__.register(float)
        def __from_timestamp(self, arg):
            self.__date = date.fromtimestamp(arg)
        @__init__.register(tuple)
        def __from_tuple(self, arg):
            if len(arg) == 3 and all(map(lambda x: isinstance(x, int), arg)):
                self.__date = date(*arg)
            else:
                raise ValueError("could not create instance from a malformed tuple")
        @__init__.register(str)
        def __from_isoformat(self, arg):
            self.__date = date.fromisoformat(arg)
        @__init__.register(datetime)
        def __from_datetime(self, arg):
            self.__date = arg.date()
        @property
        def date(self):
            return self.__date
    로그인 후 복사

    这样一来,我们便能将每种参数类型的初始化独立成一个个的方法了。

    缺点

    #1 单分派

    在调用期间应该使用哪个方法实现由分派算法决定。如果该算法只基于单个参数的类型来决定使用哪个方法实现,则称其为单分派。

    singledispatchmethod 就是就是单分派的。也就是说,只有第一个参数会作为考量。这在实际业务中是远远不足的。

    #2 不支持 typing

    然而,如上,对元组中元素类型判断还是需要我们用 if/else 实现。也就是说,我们不能使用 typing.Tuple[int, int, int]

    作为一种折中的方案,或许我们可以定义一个 ThreeIntTuple 类来对其进行限定,将这些判断从 CustomDate 类中隔离开来。

    这里仅提供一个思路让大家参考,我就不实现了(因为我们有更好的方式 xD)。

    替代方案:multimethod 库

    这个库不是标准库之一,需要通过 pip 安装:

    pip install multimethod
    로그인 후 복사

    优势

    multimethod 采用的是多分派算法,能更好地满足更复杂的场景。此外,该库对 typing 中的类型也有不错的支持。

    更更好的实践方式

    回到上面的问题,我们可以这么改进:

    • 使用 multimethod 方法来替代 singledispatchmethod

    • 使用 Tuple[int, int, int] 来替代 tuple,不再需要手动校验元组的长度和元素类型了;

    from datetime import date, datetime
    from typing import Tuple, Union
    from multimethod import multimethod
    class CustomDate:
        @multimethod
        def __init__(self, arg):
            raise TypeError("could not create instance from " + type(arg).__name__)
        @__init__.register
        def __from_timestamp(self, arg: Union[int, float]):
            self.__date = date.fromtimestamp(arg)
        @__init__.register
        def __from_tuple(self, arg: Tuple[int, int, int]):
            self.__date = date(*arg)
        @__init__.register
        def __from_isoformat(self, arg: str):
            self.__date = date.fromisoformat(arg)
        @__init__.register
        def __from_datetime(self, arg: datetime):
            self.__date = arg.date()
        @property
        def date(self):
            return self.__date
    로그인 후 복사

    究极好的实践方式(真正的方法重载)

    在此之前,先问大家一个简单的问题(这跟我们之后的内容有很大的联系):

    class A:
        def a(self):
            print(1)
        def a(self):
            print(2)
    A().a()
    로그인 후 복사

    以上这段代码会输出什么?还是会抛出错误?

    输出 2

    在 Python 中,如果定义了重名的方法,最后一个方法是会覆盖掉之前的方法的。

    但你或许不知,我们可以通过元类(metaclass)来改变这一行为:

    class MetaA(type):
        class __prepare__(dict):
            def __init__(*args):
                pass
            def __setitem__(self, key, value):
                if self.get('a'):  # Line 7
                    super().__setitem__('b', value)  # Line 8
                else:	
                    super().__setitem__(key, value)
    class A(metaclass=MetaA):
        def a(self):
            print(1)
        def a(self):
            print(2)
    A().a()  # => 1
    A().b()  # => 2  # Line 22
    로그인 후 복사

    在第 7 和第 8 行,我们将重名的 a 方法改名为 b,并在第 22 行成功地调用它了。

    multimethod 的维护者们很好地运用了这一点,对重名的方法进行了处理,以达到一种“特殊的效果”。

    回到正题,我们可以做出如下改进:

    • multimethod.multidata 设置为 CustomDate 类的元类;

    • 将所有方法命名为 __init__

        < li>

        Timestamp
    • 년, 월, 일(3개의 정수를 포함하는 튜플)

      🎜
    • 🎜ISO 형식 문자열🎜🎜
    • 🎜Datetime</code > 클래스의 일반 구현 🎜🎜 🎜🎜🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:py;">from datetime import date, datetime from typing import Tuple, Union from multimethod import multimeta class CustomDate(metaclass=multimeta): def __init__(self, arg: Union[int, float]): self.__date = date.fromtimestamp(arg) def __init__(self, arg: Tuple[int, int, int]): self.__date = date(*arg) def __init__(self, arg: str): self.__date = date.fromisoformat(arg) def __init__(self, arg: datetime): self.__date = arg.date() def __init__(self, arg): raise TypeError(&quot;could not create instance from &quot; + type(arg).__name__) @property def date(self): return self.__date</pre><div class="contentsignin">로그인 후 복사</div></div>🎜 참고: 들어오는 날짜/시간 스탬프가 합법적인지 여부에 대해서는 논의하지 않으며 유형에 대해서만 대략적인 판단을 내립니다. 🎜🎜더 좋은 방법이 있나요? 🎜🎜다양한 빌드 방법을 여러 메서드로 분할하고 <code>functoolssingledispatchmethod 데코레이터를 사용하여 전달된 매개변수 유형에 따라 호출할 메서드를 결정할 수 있습니다. 🎜rrreee🎜이런 방식으로 각 매개변수 유형의 초기화를 별도의 메소드로 분리할 수 있습니다. 🎜🎜단점🎜
      #1 단일 디스패치
      🎜호출 중에 사용해야 하는 메소드 구현은 디스패치 알고리즘에 따라 결정됩니다. 알고리즘이 단일 매개변수의 유형만을 기반으로 사용할 메소드 구현을 결정하는 경우 이를 단일 디스패치라고 합니다. 🎜🎜singledispatchmethod는 단일 디스패치입니다. 즉, 첫 번째 매개변수만 고려됩니다. 이는 실제 비즈니스에서는 충분하지 않습니다. 🎜
      #2는 입력을 지원하지 않습니다
      🎜그러나 위에서 언급한 것처럼 요소 유형을 결정하려면 if/else를 사용해야 합니다. 튜플. 즉, typing.Tuple[int, int, int]를 사용할 수 없습니다. 🎜🎜절충안으로 ThreeIntTuple 클래스를 정의하여 이를 제한하고 이러한 판단을 CustomDate 클래스에서 분리할 수 있습니다. 🎜🎜여기에는 참고용 아이디어일 뿐이며 구현하지는 않겠습니다(더 나은 방법이 있기 때문입니다). 🎜🎜대안: 멀티메소드 라이브러리🎜🎜이 라이브러리는 표준 라이브러리 중 하나가 아니며 pip를 통해 설치해야 합니다. 🎜rrreee🎜Advantages🎜🎜멀티메서드는 다중 디스패치 알고리즘을 사용하므로 더 나은 요구 사항을 충족할 수 있습니다. 더 복잡한 요구 사항 장면. 또한 라이브러리는 입력의 유형을 잘 지원합니다. 🎜🎜더 나은 연습🎜🎜위의 질문으로 돌아가서 다음과 같이 개선할 수 있습니다.🎜
      • 🎜대신 다중 메서드 메서드를 사용하세요. >singledispatchmethod; 🎜🎜
      • 🎜 Tuple을 대체하려면 Tuple[int, int, int]를 사용하세요. 더 이상 튜플을 수동으로 확인할 필요가 없습니다. )의 요소 유형: 🎜rrreee🎜위 코드는 무엇을 출력합니까? 아니면 오류가 발생할까요? 🎜🎜2를 출력하세요. 🎜🎜Python에서는 동일한 이름의 메소드가 정의되면 마지막 메소드가 이전 메소드를 덮어씁니다. 🎜🎜하지만 메타클래스를 통해 이 동작을 변경할 수 있다는 사실을 모르실 수도 있습니다. 🎜rrreee🎜 7행과 8행에서는 동일한 이름의 a 메서드 이름을 b로 바꿉니다. 22번 라인에서 성공적으로 호출합니다. 🎜🎜다중 메서드의 관리자는 이를 잘 활용하여 "특수 효과"를 달성하기 위해 중복된 이름을 가진 메서드를 처리했습니다. 🎜🎜주제로 돌아가서 다음과 같은 개선 사항을 적용할 수 있습니다. 🎜
        • 🎜 multimethod.multidataCustomDate로 설정 > 클래스의 메타클래스입니다. 🎜🎜
        • 🎜모든 메소드 이름을 __init__로 지정합니다. 🎜🎜🎜rrreee🎜효과적으로 보면 이는 정적 언어 메서드 오버로딩과 정확히 같습니다! 🎜

      위 내용은 Python 클래스에서 여러 생성자 메서드 오버로드 및 일반 메서드를 정의하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    관련 라벨:
    원천:yisu.com
    본 웹사이트의 성명
    본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
    최신 이슈
    인기 튜토리얼
    더>
    최신 다운로드
    더>
    웹 효과
    웹사이트 소스 코드
    웹사이트 자료
    프론트엔드 템플릿
    회사 소개 부인 성명 Sitemap
    PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!