[python] 수업 방법의 목적은 무엇입니까?

나는 나 자신에게 파이썬을 가르치고 있으며 가장 최근의 교훈은 파이썬이 Java가 아니라는 것이므로 모든 클래스 메소드를 함수로 바꾸는 데 시간을 보냈습니다.

staticJava에서 메소드로 수행 할 작업에 클래스 메소드를 사용할 필요가 없다는 것을 알고 있지만 이제 언제 사용할지 잘 모르겠습니다. 파이썬 클래스 메소드에 대해 찾을 수있는 모든 조언은 나와 같은 초보자 라인을 따르고 있으며 표준 문서는 논의 할 때 가장 불투명합니다.

누구나 파이썬에서 클래스 메소드를 사용하는 좋은 예가 있습니까? 아니면 클래스 메소드를 현명하게 사용할 수있는 시점을 누군가가 말해 줄 수 있습니까?



답변

클래스 메서드는 특정 인스턴스에 고유하지 않지만 여전히 어떤 방식으로 클래스를 포함하는 메서드가 필요할 때 사용됩니다. 그들에 대한 가장 흥미로운 점은 서브 클래스에 의해 재정의 될 수 있다는 것입니다. 하위 클래스는 Java의 정적 메소드 또는 Python의 모듈 레벨 함수에서는 불가능합니다.

class MyClass와 MyClass (공장, 의존성 주입 스터브 등)에서 작동하는 모듈 수준 함수가 있다면 그것을 a로 만드십시오 classmethod. 그러면 서브 클래스에서 사용할 수 있습니다.


답변

팩토리 메소드 (대체 생성자)는 실제로 클래스 메소드의 전형적인 예입니다.

기본적으로 클래스 메소드는 클래스의 네임 스페이스에 자연스럽게 맞는 메소드를 원할 때마다 적합하지만 클래스의 특정 인스턴스와 연관되지 않습니다.

예를 들어, 우수한 유니 패스 모듈에서 :

현재 디렉토리

  • Path.cwd()
    • 실제 현재 디렉토리를 리턴하십시오. 예를 들어, Path("/tmp/my_temp_dir"). 이것은 클래스 메소드입니다.
  • .chdir()
    • self를 현재 디렉토리로 만드십시오.

현재 디렉토리가 전체 프로세스이므로 cwd메소드에는 연관시킬 특정 인스턴스가 없습니다. 그러나 cwd주어진 Path인스턴스 의 디렉토리로 변경하는 것은 실제로 인스턴스 방법이어야합니다.

흠 … Path.cwd()실제로 Path인스턴스를 반환하는 것처럼 팩토리 메서드로 간주 될 수 있다고 생각합니다 …


답변

일반적인 방법은 디스패치의 세부 사항을 숨기는 데 유용 합니다. 객체의 클래스 또는 부모 클래스 중 하나에 의해 메서드가 구현 myobj.foo()되는지 걱정할 필요없이 입력 할 수 있습니다. 클래스 메소드는 이것과 정확히 유사하지만 클래스 객체를 대신 사용합니다. 고유 한 특수 버전이 필요하기 때문에 특별히 구현 되었는지 또는 부모 클래스가 호출을 처리 할 수 ​​있는지 여부 에 대해 걱정할 필요가 없습니다 .foo()myobjMyClass.foo()foo()MyClass

인스턴스가 존재할 때까지 인스턴스를 메소드 호출의 디스패치 지점으로 사용할 수 없기 때문에 실제 인스턴스를 작성하기 전에 설정 또는 계산을 수행 할 때 클래스 메소드가 필수적 입니다. SQLAlchemy 소스 코드에서 좋은 예를 볼 수 있습니다. dbapi()다음 링크에서 클래스 메소드를 살펴보십시오 .

https://github.com/zzzeek/sqlalchemy/blob/ab6946769742602e40fb9ed9dde5f642885d1906/lib/sqlalchemy/dialects/mssql/pymssql.py#L47

당신은 것을 볼 수 있습니다 dbapi()그것을 실행해야하기 때문에 데이터베이스 백엔드 사용이 주문형 필요로하는 공급 업체 특정 데이터베이스 라이브러리를 가져올 수있는 방법, 수업 방법입니다 전에 생성 받기 시작 특정 데이터베이스 연결의 경우 – 그러나 그것이 수 없어 간단한 함수 또는 정적 함수입니다. 부모 클래스보다 서브 클래스에서 더 구체적으로 작성 해야하는 다른 지원 메소드를 호출 할 수 있기를 원하기 때문입니다. 그리고 함수 나 정적 클래스로 디스패치하면 초기화를 수행하는 클래스에 대한 지식을 “잊어”버립니다.


답변

최근에는 프로그래밍 방식으로 설정할 수있는 로깅 수준에 따라 다양한 양의 출력을 출력하는 매우 가벼운 로깅 클래스가 필요했습니다. 그러나 디버깅 메시지 또는 오류 또는 경고를 출력하려고 할 때마다 클래스를 인스턴스화하고 싶지 않았습니다. 그러나 나는 또한이 로깅 기능을 캡슐화하고 전역 선언없이 재사용 할 수있게하고 싶었습니다.

그래서 나는 @classmethod이것을 달성하기 위해 클래스 변수와 데코레이터를 사용했습니다.

간단한 로깅 클래스를 사용하여 다음을 수행 할 수 있습니다.

Logger._level = Logger.DEBUG

그런 다음 내 코드에서 많은 디버깅 정보를 뱉어 내려면 간단히 코딩해야했습니다.

Logger.debug( "this is some annoying message I only want to see while debugging" )

오류가 발생할 수 있습니다

Logger.error( "Wow, something really awful happened." )

“제작”환경에서 지정할 수 있습니다

Logger._level = Logger.ERROR

이제 오류 메시지 만 출력됩니다. 디버그 메시지가 인쇄되지 않습니다.

여기 내 수업이 있습니다.

class Logger :
    ''' Handles logging of debugging and error messages. '''

    DEBUG = 5
    INFO  = 4
    WARN  = 3
    ERROR = 2
    FATAL = 1
    _level = DEBUG

    def __init__( self ) :
        Logger._level = Logger.DEBUG

    @classmethod
    def isLevel( cls, level ) :
        return cls._level >= level

    @classmethod
    def debug( cls, message ) :
        if cls.isLevel( Logger.DEBUG ) :
            print "DEBUG:  " + message

    @classmethod
    def info( cls, message ) :
        if cls.isLevel( Logger.INFO ) :
            print "INFO :  " + message

    @classmethod
    def warn( cls, message ) :
        if cls.isLevel( Logger.WARN ) :
            print "WARN :  " + message

    @classmethod
    def error( cls, message ) :
        if cls.isLevel( Logger.ERROR ) :
            print "ERROR:  " + message

    @classmethod
    def fatal( cls, message ) :
        if cls.isLevel( Logger.FATAL ) :
            print "FATAL:  " + message

그리고 약간 테스트하는 코드 :

def logAll() :
    Logger.debug( "This is a Debug message." )
    Logger.info ( "This is a Info  message." )
    Logger.warn ( "This is a Warn  message." )
    Logger.error( "This is a Error message." )
    Logger.fatal( "This is a Fatal message." )

if __name__ == '__main__' :

    print "Should see all DEBUG and higher"
    Logger._level = Logger.DEBUG
    logAll()

    print "Should see all ERROR and higher"
    Logger._level = Logger.ERROR
    logAll()


답변

대체 생성자가 전형적인 예입니다.


답변

사용자가 내 웹 사이트에 로그인하면 사용자 이름과 비밀번호로 User () 객체가 인스턴스화됩니다.

로그인 할 사용자가없는 사용자 객체가 필요한 경우 (예 : 관리자 사용자가 다른 사용자 계정을 삭제하고 싶을 수 있으므로 해당 사용자를 인스턴스화하고 delete 메소드를 호출해야합니다) :

사용자 객체를 가져 오는 클래스 메소드가 있습니다.

class User():
    #lots of code
    #...
    # more code

    @classmethod
    def get_by_username(cls, username):
        return cls.query(cls.username == username).get()

    @classmethod
    def get_by_auth_id(cls, auth_id):
        return cls.query(cls.auth_id == auth_id).get()


답변

가장 명확한 대답은 AmanKow의 것입니다. 코드 구성 방법으로 요약됩니다. 모듈의 네임 스페이스에 싸여있는 모듈 레벨 함수로 모든 것을 작성할 수 있습니다.

module.py (file 1)
---------
def f1() : pass
def f2() : pass
def f3() : pass


usage.py (file 2)
--------
from module import *
f1()
f2()
f3()
def f4():pass
def f5():pass

usage1.py (file 3)
-------------------
from usage import f4,f5
f4()
f5()

위의 절차 적 코드는 잘 구성되어 있지 않습니다. 혼란스러워하는 3 개의 모듈 만 후에 볼 수 있듯이 각 방법은 무엇입니까? 자바와 같이 함수에 긴 설명 이름을 사용할 수 있지만 여전히 코드를 관리하기가 매우 빠릅니다.

객체 지향 방식은 코드를 관리 가능한 블록으로 분류하는 것입니다. 즉, 클래스 및 객체 및 함수는 객체 인스턴스 또는 클래스와 연관 될 수 있습니다.

클래스 함수를 사용하면 모듈 수준 함수와 비교하여 코드에서 다른 수준의 나누기를 얻을 수 있습니다. 따라서 클래스 내에서 관련 기능을 그룹화하여 해당 클래스에 할당 된 작업에보다 구체적으로 지정할 수 있습니다. 예를 들어 파일 유틸리티 클래스를 만들 수 있습니다.

class FileUtil ():
  def copy(source,dest):pass
  def move(source,dest):pass
  def copyDir(source,dest):pass
  def moveDir(source,dest):pass

//usage
FileUtil.copy("1.txt","2.txt")
FileUtil.moveDir("dir1","dir2")

이 방법은보다 유연하고 유지 관리가 가능하며, 기능을 함께 그룹화하고 각 기능이하는 일에 대해보다 명확합니다. 또한 이름 충돌을 방지합니다. 예를 들어 코드에서 사용하는 다른 가져온 모듈 (예 : 네트워크 복사)에 함수 복사본이있을 수 있으므로 전체 이름 FileUtil.copy ()를 사용하면 문제가 해결되고 복사 기능이 모두 나란히 사용할 수 있습니다.