Đối số lựa chọn chức năng Python

Việc chấp nhận các đối số vị trí tùy chọn [thường được gọi là star args trong tham chiếu đến tên thông thường của tham số, *args] có thể làm cho lệnh gọi hàm rõ ràng hơn và loại bỏ nhiễu thị giác

Ví dụ: giả sử bạn muốn ghi lại một số thông tin gỡ lỗi. Với số lượng đối số cố định, bạn sẽ cần một hàm nhận thông báo và danh sách giá trị

def log[message, values]:
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', [1, 2]]
log['Hi there', []]

>>>
My numbers are: 1, 2
Hi there

Phải vượt qua một danh sách trống khi bạn không có giá trị để ghi lại là cồng kềnh và ồn ào. Sẽ tốt hơn nếu loại bỏ hoàn toàn đối số thứ hai. Bạn có thể làm điều này trong Python bằng cách thêm tiền tố vào tên tham số vị trí cuối cùng bằng *. Tham số đầu tiên cho thông điệp tường trình là bắt buộc, trong khi bất kỳ số lượng đối số vị trí nào tiếp theo là tùy chọn. Thân hàm không cần thay đổi, chỉ những người gọi mới làm

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there

Nếu bạn đã có một danh sách và muốn gọi một hàm đối số biến như nhật ký, bạn có thể thực hiện việc này bằng cách sử dụng toán tử *. Điều này hướng dẫn Python chuyển các mục từ chuỗi dưới dạng đối số vị trí

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99

Có hai vấn đề với việc chấp nhận một số lượng đối số vị trí khác nhau

Vấn đề đầu tiên là các đối số của biến luôn được chuyển thành một bộ trước khi chúng được chuyển đến hàm của bạn. Điều này có nghĩa là nếu người gọi hàm của bạn sử dụng toán tử * trên trình tạo, nó sẽ được lặp lại cho đến khi hết. Bộ kết quả sẽ bao gồm mọi giá trị từ trình tạo, điều này có thể tiêu tốn nhiều bộ nhớ và khiến chương trình của bạn gặp sự cố

def my_generator[]:
    for i in range[10]:
        yield i

def my_func[*args]:
    print[args]

it = my_generator[]
my_func[*it]

>>>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Các hàm chấp nhận *args là tốt nhất cho các tình huống mà bạn biết số lượng đầu vào trong danh sách đối số sẽ khá nhỏ. Thật lý tưởng cho các lệnh gọi hàm chuyển nhiều tên biến hoặc tên biến lại với nhau. Nó chủ yếu là để thuận tiện cho lập trình viên và khả năng đọc mã

Vấn đề thứ hai với *args là bạn không thể thêm đối số vị trí mới vào chức năng của mình trong tương lai mà không di chuyển mọi người gọi. Nếu bạn cố gắng thêm một đối số vị trí ở phía trước danh sách đối số, những người gọi hiện có sẽ bị ngắt một cách tinh vi nếu chúng không được cập nhật

def log[sequence, message, *values]:
    if not values:
        print['%s: %s' % [sequence, message]]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s: %s' % [sequence, message, values_str]]

log[1, 'Favorites', 7, 33]      # New usage is OK
log['Favorite numbers', 7, 33]  # Old usage breaks

>>>
1: Favorites: 7, 33
Favorite numbers: 7: 33

Vấn đề ở đây là lệnh gọi log thứ hai đã sử dụng 7 làm tham số thông báo vì đối số trình tự không được cung cấp. Những lỗi như thế này rất khó theo dõi vì mã vẫn chạy mà không đưa ra bất kỳ ngoại lệ nào. Để tránh hoàn toàn khả năng này, bạn nên sử dụng các đối số chỉ có từ khóa khi bạn muốn mở rộng các hàm chấp nhận *args [xem Mục 21. “Thực thi sự rõ ràng với các đối số chỉ có từ khóa”]

Những điều cần ghi nhớ

  • Các hàm có thể chấp nhận một số lượng đối số vị trí khác nhau bằng cách sử dụng *args trong câu lệnh def
  • Bạn có thể sử dụng các mục từ một chuỗi làm đối số vị trí cho một hàm với toán tử *
  • Sử dụng toán tử * với trình tạo có thể khiến chương trình của bạn hết bộ nhớ và gặp sự cố
  • Việc thêm các tham số vị trí mới vào các hàm chấp nhận *args có thể gây ra các lỗi khó tìm

mục 19. Cung cấp hành vi tùy chọn với các đối số từ khóa

Giống như hầu hết các ngôn ngữ lập trình khác, việc gọi hàm trong Python cho phép truyền đối số theo vị trí

def remainder[number, divisor]:
    return number % divisor

assert remainder[20, 7] == 6

Tất cả các đối số vị trí cho các hàm Python cũng có thể được chuyển theo từ khóa, trong đó tên của đối số được sử dụng trong một phép gán trong dấu ngoặc đơn của lệnh gọi hàm. Các đối số từ khóa có thể được chuyển theo bất kỳ thứ tự nào miễn là tất cả các đối số vị trí bắt buộc được chỉ định. Bạn có thể trộn và kết hợp các đối số từ khóa và vị trí. Các cuộc gọi này là tương đương

remainder[20, 7]
remainder[20, divisor=7]
remainder[number=20, divisor=7]
remainder[divisor=7, number=20]

Đối số vị trí phải được chỉ định trước đối số từ khóa

remainder[number=20, 7]

>>>
SyntaxError: non-keyword arg after keyword arg

Mỗi đối số chỉ có thể được chỉ định một lần

________số 8

Tính linh hoạt của các đối số từ khóa mang lại ba lợi ích đáng kể

Ưu điểm đầu tiên là các đối số từ khóa làm cho lệnh gọi hàm rõ ràng hơn đối với những người mới đọc mã. Với phần còn lại của cuộc gọi [20, 7], không rõ đối số nào là số và số nào là số chia nếu không xem xét việc triển khai phương thức số dư. Trong cuộc gọi với các đối số từ khóa, số=20 và số chia=7 làm cho nó rõ ràng ngay lập tức tham số nào đang được sử dụng cho từng mục đích

Tác động thứ hai của các đối số từ khóa là chúng có thể có các giá trị mặc định được chỉ định trong định nghĩa hàm. Điều này cho phép một chức năng cung cấp các khả năng bổ sung khi bạn cần nhưng cho phép bạn chấp nhận hành vi mặc định trong hầu hết thời gian. Điều này có thể loại bỏ mã lặp đi lặp lại và giảm tiếng ồn

Ví dụ: giả sử bạn muốn tính tốc độ chất lỏng chảy vào thùng. Nếu thùng cũng nằm trên cân, thì bạn có thể sử dụng chênh lệch giữa hai lần đo trọng lượng ở hai thời điểm khác nhau để xác định tốc độ dòng chảy

def flow_rate[weight_diff, time_diff]:
    return weight_diff / time_diff

weight_diff = 0.5
time_diff = 3
flow = flow_rate[weight_diff, time_diff]
print['%.3f kg per second' % flow]

>>>
0.167 kg per second

Trong trường hợp điển hình, sẽ rất hữu ích khi biết tốc độ dòng chảy tính bằng kilôgam trên giây. Vào những lúc khác, sẽ rất hữu ích khi sử dụng các phép đo cảm biến cuối cùng để tính gần đúng các thang thời gian lớn hơn, chẳng hạn như giờ hoặc ngày. Bạn có thể cung cấp hành vi này trong cùng một chức năng bằng cách thêm một đối số cho hệ số tỷ lệ khoảng thời gian

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there
0

Vấn đề là bây giờ bạn cần chỉ định đối số khoảng thời gian mỗi khi bạn gọi hàm, ngay cả trong trường hợp phổ biến là tốc độ dòng chảy trên giây [trong đó khoảng thời gian là 1]

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there
1

Để làm cho điều này bớt ồn ào hơn, tôi có thể đặt giá trị mặc định cho đối số khoảng thời gian

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there
2

Đối số thời gian bây giờ là tùy chọn

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there
3

Điều này hoạt động tốt đối với các giá trị mặc định đơn giản [nó trở nên phức tạp đối với các giá trị mặc định phức tạp—xem Mục 20. “Sử dụng Không có và Tài liệu để Chỉ định Đối số Mặc định Động”]

Lý do thứ ba để sử dụng các đối số từ khóa là chúng cung cấp một cách mạnh mẽ để mở rộng các tham số của hàm trong khi vẫn tương thích ngược với các trình gọi hiện có. Điều này cho phép bạn cung cấp chức năng bổ sung mà không phải di chuyển nhiều mã, giảm khả năng phát sinh lỗi

Ví dụ: giả sử bạn muốn mở rộng hàm flow_rate ở trên để tính tốc độ dòng chảy theo đơn vị trọng lượng ngoài kilôgam. Bạn có thể làm điều này bằng cách thêm một tham số tùy chọn mới cung cấp tỷ lệ chuyển đổi cho các đơn vị đo lường ưa thích của bạn

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there
4

Giá trị đối số mặc định cho units_per_kg là 1, làm cho đơn vị trọng lượng được trả về vẫn là kilôgam. Điều này có nghĩa là tất cả những người gọi hiện tại sẽ không thấy thay đổi về hành vi. Người gọi mới đến flow_rate có thể chỉ định đối số từ khóa mới để xem hành vi mới

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there
5

Vấn đề duy nhất với phương pháp này là các đối số từ khóa tùy chọn như thời gian và units_per_kg vẫn có thể được chỉ định làm đối số vị trí

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there
6

Việc cung cấp các đối số tùy chọn theo vị trí có thể gây nhầm lẫn vì không rõ giá trị 3600 và 2 là gì. 2 tương ứng với. Cách tốt nhất là luôn chỉ định các đối số tùy chọn bằng cách sử dụng tên từ khóa và không bao giờ chuyển chúng dưới dạng đối số vị trí

Những điều cần ghi nhớ

  • Đối số hàm có thể được chỉ định theo vị trí hoặc theo từ khóa
  • Các từ khóa làm rõ mục đích của từng đối số khi nó sẽ gây nhầm lẫn chỉ với các đối số vị trí
  • Các đối số từ khóa với các giá trị mặc định giúp dễ dàng thêm các hành vi mới vào một hàm, đặc biệt là khi hàm đó có các trình gọi hiện có
  • Các đối số từ khóa tùy chọn phải luôn được chuyển theo từ khóa thay vì theo vị trí

mục 20. Sử dụng Không có và Tài liệu để Chỉ định Đối số Mặc định Động

Đôi khi bạn cần sử dụng loại không tĩnh làm giá trị mặc định của đối số từ khóa. Ví dụ: giả sử bạn muốn in các thông báo ghi nhật ký được đánh dấu bằng thời gian của sự kiện đã ghi. Trong trường hợp mặc định, bạn muốn thông báo bao gồm thời gian khi chức năng được gọi. Bạn có thể thử cách tiếp cận sau, giả sử rằng các đối số mặc định được đánh giá lại mỗi khi hàm được gọi

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there
7

Dấu thời gian giống nhau vì datetime. now chỉ được thực hiện một lần duy nhất. khi chức năng được xác định. Các giá trị đối số mặc định chỉ được đánh giá một lần cho mỗi lần tải mô-đun, điều này thường xảy ra khi chương trình khởi động. Sau khi mô-đun chứa mã này được tải, ngày giờ. bây giờ đối số mặc định sẽ không bao giờ được đánh giá lại

Quy ước để đạt được kết quả mong muốn trong Python là cung cấp giá trị mặc định là Không và ghi lại hành vi thực tế trong chuỗi tài liệu [xem Mục 49. “Viết tài liệu cho mọi chức năng, lớp và mô-đun”]. Khi mã của bạn nhìn thấy giá trị đối số là Không, bạn sẽ phân bổ giá trị mặc định tương ứng

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there
8

Bây giờ dấu thời gian sẽ khác

def log[message, *values]:  # The only difference
    if not values:
        print[message]
    else:
        values_str = ', '.join[str[x] for x in values]
        print['%s: %s' % [message, values_str]]

log['My numbers are', 1, 2]
log['Hi there']  # Much better

>>>
My numbers are: 1, 2
Hi there
9

Sử dụng Không cho các giá trị đối số mặc định đặc biệt quan trọng khi các đối số có thể thay đổi. Ví dụ: giả sử bạn muốn tải một giá trị được mã hóa dưới dạng dữ liệu JSON. Nếu giải mã dữ liệu không thành công, bạn muốn một từ điển trống được trả về theo mặc định. Bạn có thể thử phương pháp này

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99
0

Vấn đề ở đây giống như datetime. bây giờ ví dụ trên. Từ điển được chỉ định cho mặc định sẽ được chia sẻ bởi tất cả các cuộc gọi để giải mã vì các giá trị đối số mặc định chỉ được đánh giá một lần [tại thời điểm tải mô-đun]. Điều này có thể gây ra hành vi cực kỳ đáng ngạc nhiên

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99
1

Bạn sẽ mong đợi hai từ điển khác nhau, mỗi từ điển có một khóa và giá trị. Nhưng sửa đổi cái này dường như cũng sửa đổi cái kia. Thủ phạm là cả foo và bar đều bằng tham số mặc định. Chúng là cùng một đối tượng từ điển

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99
2

Cách khắc phục là đặt giá trị mặc định của đối số từ khóa thành Không và sau đó ghi lại hành vi trong chuỗi tài liệu của hàm

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99
3

Bây giờ, chạy cùng mã kiểm tra như trước sẽ tạo ra kết quả như mong đợi

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99
4

Những điều cần ghi nhớ

  • Đối số mặc định chỉ được đánh giá một lần. trong khi định nghĩa chức năng tại thời điểm tải mô-đun. Điều này có thể gây ra các hành vi kỳ lạ đối với các giá trị động [như {} hoặc []]
  • Sử dụng Không làm giá trị mặc định cho các đối số từ khóa có giá trị động. Ghi lại hành vi mặc định thực tế trong chuỗi tài liệu của hàm

mục 21. Thực thi rõ ràng với các đối số chỉ từ khóa

Truyền đối số theo từ khóa là một tính năng mạnh mẽ của các hàm Python [xem Mục 19. “Cung cấp hành vi tùy chọn với các đối số từ khóa”]. Tính linh hoạt của các đối số từ khóa cho phép bạn viết mã rõ ràng cho các trường hợp sử dụng của bạn

Ví dụ: giả sử bạn muốn chia một số cho một số khác nhưng hãy cẩn thận với các trường hợp đặc biệt. Đôi khi bạn muốn bỏ qua các ngoại lệ ZeroDivisionError và trả về vô cùng thay thế. Những lần khác, bạn muốn bỏ qua các ngoại lệ OverflowError và thay vào đó trả về 0

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99
5

Sử dụng chức năng này rất đơn giản. Cuộc gọi này sẽ bỏ qua lỗi tràn float từ phép chia và sẽ trả về 0

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99
6

Cuộc gọi này sẽ bỏ qua lỗi chia cho 0 và sẽ trả về vô cùng

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99
7

Vấn đề là rất dễ nhầm lẫn vị trí của hai đối số Boolean kiểm soát hành vi bỏ qua ngoại lệ. Điều này có thể dễ dàng gây ra các lỗi khó theo dõi. Một cách để cải thiện khả năng đọc của mã này là sử dụng đối số từ khóa. Theo mặc định, chức năng có thể quá thận trọng và luôn có thể tăng lại các ngoại lệ

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99
8

Sau đó, người gọi có thể sử dụng các đối số từ khóa để chỉ định cờ bỏ qua nào họ muốn lật cho các hoạt động cụ thể, ghi đè hành vi mặc định

favorites = [7, 33, 99]
log['Favorite colors', *favorites]

>>>
Favorite colors: 7, 33, 99
9

Vấn đề là, vì các đối số từ khóa này là hành vi tùy chọn, nên không có gì bắt buộc người gọi hàm của bạn sử dụng đối số từ khóa cho rõ ràng. Ngay cả với định nghĩa mới của safe_division_b, bạn vẫn có thể gọi nó theo cách cũ với các đối số vị trí

def my_generator[]:
    for i in range[10]:
        yield i

def my_func[*args]:
    print[args]

it = my_generator[]
my_func[*it]

>>>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
0

Với các chức năng phức tạp như thế này, tốt hơn hết là yêu cầu người gọi rõ ràng về ý định của họ. Trong Python 3, bạn có thể yêu cầu sự rõ ràng bằng cách xác định các hàm của mình bằng các đối số chỉ có từ khóa. Các đối số này chỉ có thể được cung cấp theo từ khóa, không bao giờ theo vị trí

Ở đây, tôi định nghĩa lại hàm safe_division để chấp nhận đối số chỉ từ khóa. Biểu tượng * trong danh sách đối số cho biết phần cuối của đối số vị trí và phần đầu của đối số chỉ từ khóa

def my_generator[]:
    for i in range[10]:
        yield i

def my_func[*args]:
    print[args]

it = my_generator[]
my_func[*it]

>>>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1

Bây giờ, gọi hàm với các đối số vị trí cho các đối số từ khóa sẽ không hoạt động

def my_generator[]:
    for i in range[10]:
        yield i

def my_func[*args]:
    print[args]

it = my_generator[]
my_func[*it]

>>>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2

Đối số từ khóa và giá trị mặc định của chúng hoạt động như mong đợi

def my_generator[]:
    for i in range[10]:
        yield i

def my_func[*args]:
    print[args]

it = my_generator[]
my_func[*it]

>>>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3

Đối số chỉ từ khóa trong Python 2

Thật không may, Python 2 không có cú pháp rõ ràng để chỉ định các đối số chỉ từ khóa như Python 3. Nhưng bạn có thể đạt được cùng một hành vi tăng TypeErrors cho các lệnh gọi hàm không hợp lệ bằng cách sử dụng toán tử ** trong danh sách đối số. Toán tử ** tương tự như toán tử * [xem Tiết 18. “Reduce Visual Noise with Variable Positional Arguments”], ngoại trừ việc thay vì chấp nhận số lượng đối số vị trí thay đổi, nó chấp nhận bất kỳ số lượng đối số từ khóa nào, ngay cả khi chúng không được xác định

def my_generator[]:
    for i in range[10]:
        yield i

def my_func[*args]:
    print[args]

it = my_generator[]
my_func[*it]

>>>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4

Để safe_division nhận các đối số chỉ có từ khóa trong Python 2, bạn có hàm accept **kwargs. Sau đó, bạn bật các đối số từ khóa mà bạn mong đợi từ từ điển kwargs, sử dụng đối số thứ hai của phương thức pop để chỉ định giá trị mặc định khi thiếu khóa. Cuối cùng, bạn đảm bảo rằng không còn đối số từ khóa nào trong kwargs để ngăn người gọi cung cấp đối số không hợp lệ

4 loại đối số trong Python là gì?

Trong Python, chúng ta có 4 loại đối số hàm sau. .
Đối số mặc định
Đối số từ khóa [đối số được đặt tên]
đối số vị trí
Đối số tùy ý [đối số có độ dài thay đổi *args và **kwargs ]

3 loại đối số trong Python là gì?

Do đó, chúng tôi kết luận rằng Đối số hàm Python và ba loại đối số hàm của nó. Đây là- các đối số mặc định, từ khóa và tùy ý .

Bạn có thể sử dụng các hàm làm đối số trong Python không?

Vì hàm là đối tượng nên chúng ta có thể chuyển chúng làm đối số cho các hàm khác . Các hàm có thể nhận các hàm khác làm đối số còn được gọi là hàm bậc cao. Trong ví dụ bên dưới, một hàm hello được tạo lấy một hàm làm đối số.

* arg trong Python là gì?

*args cho phép chúng tôi chuyển một số lượng biến đối số không phải từ khóa cho hàm Python . Trong hàm, chúng ta nên sử dụng dấu hoa thị [ * ] trước tên tham số để truyền số lượng đối số thay đổi.

Chủ Đề