Featured image of post  Bạn sẽ học được gì - tải game bắn cá đổi thưởng tiền mặt

Bạn sẽ học được gì - tải game bắn cá đổi thưởng tiền mặt

Tải game bắn cá đổi thưởng tiền mặt, trải nghiệm game sôi động và thú vị.

Chaofa p88 nhà cái Yuan, ngày 11 tháng 12 năm 2023, khoảng 5 phút đọc python-type-challenge python-typing-tutorial python-type-tutorial

Nội dung trang này:

  • Hướng dẫn đọc

  • Các kiểu nâng cao trong Python Type

  • * Protocol - Giao thức
    
    • Tái định nghĩa
    •   * override - Tái định nghĩa phương thức lớp
      
      • overload - Tái định nghĩa chữ ký hàm
    • ForwardRef - Kiểu tham chiếu trước
    • Generator - Bộ sinh
    • Never
    • TypeGuard
    • TupleVar
    • ParamSpec
  • Đối tượng độc giả

    • Có nền tảng Python cơ bản và cần phát triển dự án lớn hơn.
    • Người đã có kinh nghiệm lập trình với ngôn ngữ có kiểu tĩnh khác và muốn hiểu nhanh về chú thích kiểu trong Python (type hint).
    • Nếu bạn chưa quen, hãy đọc hai bài đầu tiên trước.
    • Bạn sẽ học được gì?
      • Cách Python định nghĩa protocol.
      • Làm thế nào để tái định nghĩa phương thức lớp và chữ ký hàm trong Python.
      • Sử dụng các kiểu như tham chiếu trước, bộ sinh, Never,…
      • Khuyến khích tự hoàn thành các bài tập trên Python-Type-Challenges.

Bài viết này dựa theo phân loại của thư viện Python-Type-Challenges[1], gồm bốn phần:

  • Bài Tập Thể Dục Kiểu Python (Một) – Cơ Bản
  • Bài Tập Thể Dục Kiểu Python (Hai) – Trung Cấp
  • Bài Tập Thể Dục Kiểu Python (Ba) – Nâng Cao (bài này)
  • Bài Tập Thể Dục Kiểu Python (Tư) – Cực Kỳ Nâng Cao (chưa làm được)

Các kiểu nâng cao trong Python Type

Protocol - Giao Thức

Protocol giống cách định nghĩa abc, biểu thị rằng kiểu này có những phương thức nhất định. sunvip.club * Ví dụ: Lớp Duck có phương thức quack.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from typing import Protocol
# SupportsQuack là kiểu có phương thức quack
class SupportsQuack(Protocol):
  def quack(self) -> None:
    ...
class Duck:
  def quack(self) -> None:
    print('quack!')
duck: SupportsQuack = Duck() # Đúng
class Dog:
  def bark(self) -> None:
    print("bark!")
dog: SupportsQuack = Dog()  # Sai, vì lớp dog không có phương thức `quack`

Tái Định Nghĩa

override - Tái định nghĩa phương thức lớp

Tính năng này rất phổ biến trong các ngôn ngữ khác, cho phép con lớp tái định nghĩa phương thức từ lớp cha. Xem ví dụ sau:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Animal:
  def say(self) -> str:
    return 'hello world'
class Dog(Animal):
  # Đúng
  def say(self) -> str:
    return "bake bake!!"
class Duck(Animal):
  # Có thể tên phương thức bị sai, nhưng trình kiểm tra kiểu cũng không báo lỗi
  def sey(self) -> str:
    return "quack quack!!"
animal1: Animal = Dog()
animal1.say() # Trả về 'bake bake!!', đã tái định nghĩa phương thức say
animal2: Animal = Duck()
animal2.say() # Trả về 'hello world', vì không tái định nghĩa đúng say

Với từ khóa override, vấn đề trên sẽ không xảy ra:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Animal:
  def say(self) -> str:
    return 'hello world'
class Dog(Animal):
  # Đúng, tái định nghĩa phương thức Animal.say
  @override
  def say(self) -> str:
    return "bake bake!!"
class Duck(Animal):
  # Báo lỗi ở đây vì Animal không có phương thức sey
  @override
  def sey(self) -> str:
    return "quack quack!!"

overload - Tái định nghĩa chữ ký hàm

Điều này không phải là tái định nghĩa thực sự, mà chỉ đơn giản là tái định nghĩa chữ ký mà không cần cài đặt thực tế.

  • Snippet code dưới đây lấy từ Python-Type-Challenges
    • process không được cài đặt lại thật sự
    • overload phải trước khi thực hiện process
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from typing import overload
@overload
def process(response: None) -> None:
  ...
@overload
def process(response: int) -> tuple[int, str]:
  ...
@overload
def process(response: bytes) -> str:
  ...
def process(response: int | bytes | None) -> str | None | tuple[int, str]:
  ...

from typing import assert_type
assert_type(process(b"42"), str)
assert_type(process(42), tuple[int, str])
assert_type(process(None), None)
assert_type(process(42), str) # expect-type-error
assert_type(process(None), str) # expect-type-error
assert_type(process(b"42"), tuple[int, str]) # expect-type-error
assert_type(process(None), tuple[int, str]) # expect-type-error
assert_type(process(42), str) # expect-type-error
assert_type(process(None), str) # expect-type-error

ForwardRef - Tham Chiếu Trước

  • Example 1, Khi sử dụng một kiểu mà nó chưa được định nghĩa hoàn chỉnh nhưng chúng ta vẫn muốn định nghĩa giá trị trả về. Lúc này cần dùng tham chiếu trước, cú pháp là «biến bọc trong dấu ngoặc kép», ví dụ copy trả về "MyClass".
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class MyClass:
  def __init__(self, x: int) -> None:
    self.x = x
  def copy(self) -> "MyClass":
    copied_object = MyClass(x=self.x)
    return copied_object
from typing import assert_type
inst = MyClass(x=1)
assert_type(inst.copy(), MyClass)
# Hai kiểu này là cùng nhau
# Tham chiếu trước thường sử dụng dấu ngoặc kép để bọc tên lớp.
  • Example 2, Định nghĩa vòng lặp kiểu. Định nghĩa một từ điển gọi là Tree, key là str, value là Tree.
1
type Tree = dict[str, "Tree"]

Generator - Bộ Sinh

Cú pháp: Generator[YieldType, SendType, ReturnType], chi tiết xem game winvn ví dụ:

1
2
3
4
5
6
7
8
9
def echo_round() -> Generator[int, float, str]:
  sent = yield 0
  while sent >= 0:
    sent = yield round(sent)
  return 'Done'
# Giải thích:
# yield sau đều trả về kiểu int
# hàm round nhận một float
# return cuối cùng là 'Done', kiểu là str

Never

Thông thường dùng để biểu thị một hàm sẽ không bao giờ được gọi hoặc không có giá trị trả về.

  • Example 1, Không bao giờ được gọi
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from typing import Never
def never_call_me(arg: Never) -> None:
  pass
def int_or_str(arg: int | str) -> None:
  never_call_me(arg) # Báo lỗi trình kiểm tra kiểu
  match arg:
    case int():
      print("Là số nguyên")
    case str():
      print("Là chuỗi")
    case _:
      never_call_me(arg) # OK, arg thuộc kiểu Never
1
2
3
4
5
from typing import Never
def stop() -> Never:
  raise RuntimeError("")
from typing import assert_never
assert_never(stop())

TypeGuard

Dùng để thu hẹp kiểu Python. Khi định nghĩa bằng TypeGuard, trình kiểm tra kiểu sẽ biết hai thông tin:

  • Giá trị trả về là kiểu boolean.
  • Nếu trả về True, thì kiểu là kiểu bên trong TypeGuard.
1
2
3
from typing import Any, TypeGuard
def is_string(value: Any) -> TypeGuard[str]:
  return isinstance(value, str)

TupleVar

Sử dụng nâng cao của Generic, biểu thị chấp nhận nhiều tham số parameterized generic.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def move_first_element_to_lastT, *Ts -> tuple[*Ts, T]:
  return (*tup[1:], tup[0])
# T được ràng buộc là int, Ts được ràng buộc là ()
# Kiểu cuối cùng là tuple[int], giá trị trả về là (1, )
move_first_element_to_last(tup=(1,))
# T được ràng buộc là int, Ts được ràng buộc là (str, )
# Giá trị trả về là ('spam', 1), kiểu trả về là tuple[str, int]
move_first_element_to_last(tup=(1, 'spam'))
# T ràng buộc là int, Ts ràng buộc là (str, float)
# Giá trị trả về là ('spam', 3.0, 1), kiểu trả về là tuple[str, float, int]
move_first_element_to_last(tup=(1, 'spam', 3.0))
# Cả kiểm tra kiểu và chạy đều sai, cần ít nhất một giá trị
# tuple[()] và tuple[T, *Ts] khác nhau
move_first_element_to_last(tup=())

ParamSpec

Cũng là cách sử dụng nâng cao của Generic, thường dùng để truyền tham số. Thường áp dụng cho truyền và sửa đổi tham số của hàm bậc cao, ví dụ như decorator nhận vào một hàm, ví dụ cụ thể:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from collections.abc import Callable
import logging
def add_loggingT, **P -> Callable[P, T]:
  '''Trình trang trí an toàn về kiểu để thêm ghi log vào hàm.'''
  def inner(*args: P.args, **kwargs: P.kwargs) -> T:
    logging.info(f'{f.__name__} was called')
    return f(*args, **kwargs)
  return inner
@add_logging
def add_two(x: float, y: float) -> float:
  return x + y

Nếu không có ParamSpec, chỉ có thể viết Callable[..., Any], kiểu này chỉ biết đó là một hàm, không suy luận được kiểu cụ thể của hàm.

[^1]. Python-Type-Challenges

Built with Hugo
Theme Stack thiết kế bởi Jimmy