Typescript와 같은 Python 코드 작성

王林
풀어 주다: 2024-08-01 20:18:11
원래의
667명이 탐색했습니다.

이 글을 읽고 싶은 분들은 typescript가 무엇인지 아실 거라고 생각합니다. Javascript 개발자는 Javascript를 더욱 안전하게 만들기 위해 TypeScript를 만들었습니다. Typesafe는 코드를 더 읽기 쉽게 만들고 테스트를 작성하지 않고도 버그가 적습니다. Python에서 유형 안전성을 달성할 수 있습니까?.

왜 유형 안전성이 필요한가요?

이 청순해 보이는 기능을 상상해 보세요

def send_email(sender, receiver, message):
    ...
로그인 후 복사

코드 구현을 의도적으로 숨겼습니다. 함수 이름과 매개변수만으로 이 함수를 사용하기 위해 필요한 함수가 무엇인지, 어떤 매개변수가 필요한지 짐작할 수 있나요? 우리는 기능 이름을 통해 이메일을 보내는 기능이라는 것을 알고 있습니다. 매개변수는 어떻습니까? 이 함수를 사용하려면 무엇을 넣어야 할까요?

첫 번째 추측 보낸 사람은 이메일의 문자열, 수신자는 이메일의 문자열, 메시지는 이메일 본문의 문자열입니다.

send_email(sender="john@mail.com", receiver="doe@mail.com", message="Hello doe! How are you?")
로그인 후 복사

가장 간단한 추측입니다. 하지만 이것이 유일한 추측은 아닙니다.

두 번째 추측 발신자는 db의 user_id 정수이고 수신자는 db의 user_id 정수이며 메시지는 이메일 본문의 문자열입니다.

john_user_id = 1
doe_user_id = 2
send_email(sender=1, receiver=2, message="Hello doe! How are you?")
로그인 후 복사

애플리케이션 작업을 상상해 보세요. 대부분의 응용 프로그램은 일부 데이터베이스를 사용합니다. 사용자는 일반적으로 ID로 나타냅니다.

세 번째 추측은 발신자가 사전, 수신자가 사전, 메시지가 사전입니다.

john = {
    "id": 1,
    "username": "john",
    "email": "john@mail.com"
}
doe = {
    "id": 2,
    "username": "doe",
    "email": "doe@mail.com"
}
message = {
    "title": "Greeting my friend doe",
    "body": "Hello doe! How are you?"
}
send_email(sender=john, receiver=doe, message=message)
로그인 후 복사

send_email에는 이메일과 사용자 ID 이상의 것이 필요할 수도 있습니다. 각 매개변수에 더 많은 데이터를 추가하기 위해 일부 사전 구조가 사용되었습니다. 메시지는 단지 str이 아니라 제목과 본문이 필요할 수도 있다는 것을 알 수 있습니다.

네 번째 추측 발신자는 User 클래스, 수신자는 User 클래스, 메시지는 사전입니다.

class User():

    def __init__(self, id, username, email):
        self.id = id
        self.username = username
        self.email = email

john = User(id=1, username="john", email="john@mail.com")
doe = User(id=2, username="doe", email="doe@mail.com")
message = {
    "title": "Greeting my friend doe",
    "body": "Hello doe! How are you?"
}
send_email(sender=john, receiver=doe, message=message)
로그인 후 복사

send_email은 Django ORM 또는 Sqlalchemy와 같은 일부 데이터베이스 Orm과 통합될 수 있습니다. 최종 사용자의 편의를 위해 ORM 클래스를 직접 사용하였습니다.

그럼 정답은 무엇인가요? 그 중 하나가 정답이 될 수 있습니다. 어쩌면 정답은 두 가지 추측의 조합일 수도 있습니다. 발신자 및 수신자는 User 클래스(4번째 추측)이지만 메시지는 str(첫 번째 추측)입니다. 코드 구현을 읽지 않으면 확신할 수 없습니다. 최종 사용자라면 시간 낭비입니다. 이 기능을 사용하는 최종 사용자로서 우리는 어떤 기능이 수행되고, 어떤 매개변수가 필요한지, 어떤 기능 출력이 무엇인지만 알면 됩니다.

해결책

독스트링

Python은 docstring을 사용하여 fuction 문서를 내장했습니다. 여기에 독스트링 예제가 있습니다.

def add(x, y):
    """Add two number

    Parameter:\n
    x -- int\n
    y -- int\n

    Return: int
    """
    return x + y

def send_email(sender, receiver, message):
    """Send email from sender to receiver

    Parameter:\n
    sender -- email sender, class User\n
    receiver -- email receiver, class User\n
    message -- body of the email, dictionary (ex: {"title": "some title", "body": "email body"}\n

    Return: None
    """
    ...
로그인 후 복사

Docstring의 장점은 편집기와 호환된다는 것입니다. vscode에서는 함수 위로 마우스를 가져가면 docstring이 표시됩니다. Python의 대부분의 라이브러리는 해당 기능을 문서화하기 위해 docstring을 사용합니다.

Writing Python code like Typescript

Docstring의 문제는 문서 동기화입니다. Docstring이 항상 코드 구현과 동기화되는지 확인하는 방법. 테스트에 바로 넣을 수는 없습니다. 인터넷에서 어떤 사람으로부터 "문서가 오래된 것은 문서가 없는 것보다 더 나쁘다"는 말을 들었습니다.

Doctest

그런데 doctest를 사용하여 docstring을 테스트할 수 있습니다. Doctest는 귀하의 Docstring에서 example을 실행하여 귀하의 Docstring을 테스트합니다. Doctest는 Python에 이미 사전 설치되어 있으므로 외부 종속성이 필요하지 않습니다. 이 예를 보고 my_math.py라는 새 파일을 만든 다음 이 코드를 입력하세요.

# my_math.py
def add(x, y):
    """Add two integer

    Parameter:\n
    x -- int\n
    y -- int\n

    Return: int
    >>> add(1, 2)
    3
    """
    return x + y


if __name__ == "__main__":
    import doctest

    doctest.testmod()

로그인 후 복사

docstring example과 동일한 코드이지만 코드 마지막 줄에 example과 doctest를 추가했습니다. 독스트링을 테스트하려면 python my_math.py 파일을 실행하세요. 출력이 없으면 예제가 테스트를 통과했다는 의미입니다. 출력을 보려면 verbose 모드에서 python my_math.py -v를 실행하면 이 출력이 표시됩니다.

Trying:
    add(1, 2)
Expecting:
    3
ok
1 items had no tests:
    __main__
1 items passed all tests:
   1 tests in __main__.add
1 tests in 2 items.
1 passed and 0 failed.
Test passed
로그인 후 복사

코드 예제에 실수가 있으면 오류가 반환됩니다.

# my_math.py
def add(x, y):
    """Add two integer

    Parameter:\n
    x -- int\n
    y -- int\n

    Return: int
    >>> add(2, 2) # <-- I change it here
    3
    """
    return x + y


if __name__ == "__main__":
    import doctest

    doctest.testmod()

로그인 후 복사

출력:

**********************************************************************
File "~/typescript-in-python/my_math.py", line 12, in __main__.add
Failed example:
    add(2, 2) # <-- I change it here
Expected:
    3
Got:
    4
**********************************************************************
1 items had failures:
   1 of   1 in __main__.add
***Test Failed*** 1 failures.
로그인 후 복사

좋아요! 이제 내 독스트링을 테스트할 수 있습니다. 하지만 주의사항은 다음과 같습니다.

  1. doctest는 예시만 확인 주석 함수 매개변수는 확인하고 반환하지 않습니다
  2. doctest는 코드가 올바른지 확인하기 위해 다른 테스트 도구처럼 코드를 실행해야 합니다 doctest는 코드 예제를 실행해야 합니다. 코드에 데이터베이스나 smtp 서버(예: 이메일 보내기)와 같은 외부 도구가 필요한 경우 doctest를 사용하여 테스트하기가 어렵습니다.

파이썬 타이핑

코드가 올바른지 확인하기 위해 코드를 실행할 필요가 없는 경우도 있습니다. 입력 유형과 출력 유형만 있으면 됩니다. 어떻게? 이 예를 고려해보세요.

def add(x, y):
    """Add two integer

    Parameter:\n
    x -- int\n
    y -- int\n

    Return: int
    """
    return x + y

def sub(x, y):
    """Substract two integer

    Parameter:\n
    x -- int\n
    y -- int\n

    Return: int
    """
    return x - y

a = add(2, 1)
b = add(1, 1)
c = sub(a, b)
로그인 후 복사

함수 add는 int를 반환하고 function sub에는 입력 매개변수로 두 개의 int가 필요합니다. 추가 함수에서 두 개의 반환을 사용하고 위의 예와 같이 하위 매개변수에 넣으면 오류가 발생합니까? 물론 하위 함수에 int가 필요하고 int도 넣었기 때문이 아닙니다.

Python 3.5부터 Python에는 타이핑이라는 유형이 내장되어 있습니다. 입력을 사용하면 아래 예와 같이 함수에 유형을 추가할 수 있습니다.

def add(x: int, y: int) -> int:
    """Add two integer"""
    return x + y

a = add(1, 2)
로그인 후 복사

Instead put it on your docstring you put it on the function. Typing is supported on many editor. If you use vscode you can hover on variable and it will shown it's type.
Writing Python code like Typescript

Nice now our code will have a type safety. eeehhhh not realy. If I intentionally use function incorrectlly like this.

def add(x: int, y: int) -> int:
    """Add two integer"""
    return x + y

res = add(1, [])
print(res)
로그인 후 복사

It will show error

Traceback (most recent call last):
  File "~/typescript-in-python/main.py", line 5, in <module>
    res = add(1, [])
          ^^^^^^^^^^
  File "~/typescript-in-python/main.py", line 3, in add
    return x + y
           ~~^~~
TypeError: unsupported operand type(s) for +: 'int' and 'list'
로그인 후 복사

But it doesn't show that you put incorrect type. Even worse if you use it like this.

def add(x: int, y: int) -> int:
    """Add two integer"""
    return x + y

res = add("hello", "world")
print(res)
로그인 후 복사
로그인 후 복사

It will succeed. It must be error because you put incorrect type.

helloworld
로그인 후 복사

Why python typing doesn't have type checker by default??. Based on pep-3107 it said

Before launching into a discussion of the precise ins and outs of Python 3.0’s function annotations, let’s first talk broadly about what annotations are and are not:

  1. Function annotations, both for parameters and return values, are completely optional.
  2. Function annotations are nothing more than a way of associating arbitrary Python expressions with various parts of a function at compile-time. By itself, Python does not attach any particular meaning or significance to annotations. Left to its own, Python simply makes these expressions available as described in Accessing Function Annotations below.

The only way that annotations take on meaning is when they are interpreted by third-party libraries. ...

So in python typing is like a decorator in typescript or java it doesn't mean anything. You need third party libraries todo type checking. Let's see some library for typechecking.

Python typing + type checker

Here are libraries for typechecking in python. For example we will typecheck this wrong.py file

def add(x: int, y: int) -> int:
    """Add two integer"""
    return x + y

res = add("hello", "world")
print(res)
로그인 후 복사
로그인 후 복사

1.mypy

The "OG" of python type checker. To install it just using pip pip install mypy. Now let's use mypy to typecheck this file. Run mypy wrong.py. It will shown type error which is nice.

wrong.py:5: error: Argument 1 to "add" has incompatible type "str"; expected "int"  [arg-type]
wrong.py:5: error: Argument 2 to "add" has incompatible type "str"; expected "int"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)
로그인 후 복사

btw you can run mypy on entire project by using mypy ..

2.pyright

Another typechecker is pyright. It created by microsoft. It's same like mypy install through pip pip install pyright. Then run it pyright wrong.py. It will shown this error.

~/typescript-in-python/wrong.py
  ~/typescript-in-python/wrong.py:5:11 - error: Argument of type "Literal['hello']" cannot be assigned to parameter "x" of type "int" in function "add"
    "Literal['hello']" is incompatible with "int" (reportArgumentType)
  ~/typescript-in-python/wrong.py:5:20 - error: Argument of type "Literal['world']" cannot be assigned to parameter "y" of type "int" in function "add"
    "Literal['world']" is incompatible with "int" (reportArgumentType)
2 errors, 0 warnings, 0 informations
로그인 후 복사

It said that it's more faster than mypy but I found that's not much diffrent. Maybe my code base it's to small. Also pyright implement more python standard than mypy you can see on https://microsoft.github.io/pyright/#/mypy-comparison. Personaly I prefer mypy than pyright because the error message were more readable.

3.pylyzer

Speaking of performance and speed another new python typechecker pylyzer. It's written in rust. You can install it through pip pip install pylyzer or through cargo (rust package manager) cargo install pylyzer --locked. Then run it pylyzer wrong.py. It will shown this error.

Start checking: wrong.py
Found 2 errors: wrong.py
Error[#2258]: File wrong.py, line 5, <module>.res

5 | res = add("hello", "world")
  :           -------
  :                 |- expected: Int
  :                 `- but found: {"hello"}

TypeError: the type of add::x (the 1st argument) is mismatched

Error[#2258]: File wrong.py, line 5, <module>.res

5 | res = add("hello", "world")
  :                    -------
  :                          |- expected: Int
  :                          `- but found: {"world"}

TypeError: the type of add::y (the 2nd argument) is mismatched
로그인 후 복사

So far this is the most readable and beautiful error message. It's reminds me of rust compiler error. Speed, performance and most readable error message, I think I will choose to using pylyzer if the package already stable. The problem is at the time I write this blog, pylyzer still in beta. It can only typecheck your code base, it haven't support external depedencies.

Conclusion

Alright we successfully write python code like typescript (kinda). There is more way to using python typing module other than check simple type (str, int, bool etc). Maybe I will cover more advance type it in next blog. Maybe you guys have opinion about this, know better typechecker other then those 3, found other way to do typecheck in python or other. let me know on comment section below. As always Happy Coding.

위 내용은 Typescript와 같은 Python 코드 작성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿