Truyền đối số vào hàm trong Python

Table of Content

Giá trị bạn truyền vào một hàm khi gọi hàm được gọi là đối số (argument). Khi bạn gọi hàm với các đối số, giá trị của các đối số này được sao chép cho các tham số bên trong hàm. Ví dụ:

>>> def echo(anything):
        return anything
>>> echo('Rumplestiltskin')
'Rumplestiltskin'

Trong ví dụ này, hàm echo() được gọi với đối số là chuỗi 'Rumplestiltskin'. Giá trị này được sao chép cho tham số anything và sau đó được trả về.

Đối số vị trí

Là các đối số được sao chép cho các tham số tương ứng theo một thứ tự nhất định. Đây là cách truyền đối số đơn giản nhất cho một hàm trong Python.

Hàm sau tạo nên một từ điển từ các đối số vị trí của nó và trả về từ điển này.

>>> def menu(wine, entree, dessert):
        return {'wine': wine, 'entree': entree, 'dessert': dessert}
>>> menu('chardonnay', 'chicken', 'cake') 
{'wine': 'chardonnay', 'entree': chicken, 'dessert': 'cake'}

Mặc dù là cách truyền đối số phổ biến và đơn giản, nhược điểm của các đối số vị trí là bạn buộc phải nhớ ý nghĩa của mỗi vị trí. Nếu chúng ta quên và gọi menu() với wine là đối số cuối cùng thay vì đầu tiên, kết quả sẽ rất khác.

>>> menu('beef', 'bagel', 'bordeaux')
{'wine': 'beef', 'entree': 'bagel', 'dessert': 'bordeaux'}

Đối số có tên

Để tránh sự nhầm lẫn khi dùng đối số vị trí, bạn có thể chỉ định đối số bằng tên của các tham số tương ứng của chúng, thậm chí theo thứ tự khác với thứ tự được định nghĩa trong hàm.

>>> menu(entree='beef', dessert='bagel', wine='bordeaux')
{'wine': 'bordeaux', 'entree': 'beef', 'dessert': 'bagel'}

Khi dùng đồng thời cả đối số vị trí lẫn đối số có tên, đối số vị trí phải đặt trước.

>>> menu('frontenac', dessert='flan', entree='fish')
{'wine': 'frontenac', 'entree': 'fish', 'dessert': 'flan'}

Gom các đối số vị trí bằng *

Ta có thể gom tất cả các đối số vị trí vào trong một tham số bằng cách đặt * trước tên tham số ấy. Theo quy ước của Python, tham số ấy thường có tên là args, đôi khi cũng được đặt là params.

args là một tuple mà các phần tử là các đối số vị trí được truyền vào hàm khi gọi hàm.

Ví dụ:

>>> def print_args(*args):
    print('Positional tuple', args)
>>> print_args()
Positional tuple ()
>>> print_args(3,2,1,'wait!', 'uh...')
Positional tuple (3, 2, 1, 'wait!', 'uh...')

Khi tham số *args được dùng chung với các tham số khác, nó phải đặt ở vị trí cuối cùng, và args sẽ chứa các đối số vị trí từ vị trí đặt *args trở đi. Ví dụ:

>>> def print_more(required1, required2, *args):
    print('Need this one:', required1)
    print('Need this one too:', required2)
    print('All the rest:', args)
>>> print_more('cap', 'gloves', 'scarf', 'monocle', 'mustache wax')
Need this one: cap
Need this one too: gloves
All the rest: ('scarf', 'monocle', 'mustache wax')

Không chỉ được sử dụng trong định nghĩa hàm * cũng có thể được sử dụng khi gọi hàm.

Sử dụng * đặt trước một tuple, bạn chuyển tuple này thành danh sách các đối số vị trí để truyền vào hàm.

Ví dụ sau minh họa cách dùng * khi gọi hàm.

>>> def print_args(*args):
    print('Positional tuple', args)
>>> print_args(2, 5, 7, 'x')
Positional tuple (2, 5, 7, 'x')
>>> args = (2, 5, 7, 'x')
>>> print_args(args)
Positional tuple ((2, 5, 7, 'x'),)
>>> print_args(*args)
Positional tuple (2, 5, 7, 'x')
  • Bên ngoài hàm *args phân rã tuple arguments thành danh sách các đối số vị trí.
  • Bên trong hàm, *args gom tất cả các đối số vị trí vào trong tuple args.
  • Bên trong hàm bạn có thể dùng *params thay vì *args vì thực ra nó là tham số, nhưng người ta thường dùng *args cả trong hàm lẫn ngoài hàm.

Gom các đối số có tên bằng **

Bạn có thể sử dụng hai dấu sao ** để gom các đối số có tên vào một từ điển, ở đó tên của đối số là khóa còn giá trị của đối số là giá trị của khóa. Tham số này trong hàm thường được gọi là kwargs.

Ví dụ:

>>> def print_kwargs(**kwargs):
    print('Keyword arguments:', kwargs)
>>> print_kwargs()
Keyword arguments: {}
>>> print_kwargs(wine='merlot', entree='mutton', dessert='macaroon')
Keyword arguments: {'wine': 'merlot', 'entree': 'mutton', 'dessert': 'macaroon'}

Thứ tự các tham số:

  1. Các tham số bắt buộc.
  2. Tham số *args
  3. Tham số **kwargs

Bên ngoài hàm **kwargs phân rã một từ điển thành danh sách các đối số có tên.

Chỉ cho phép đối số có tên

Với một tham số, ta có thể truyền giá trị cho nó bằng đối số vị trí cũng như đối số có tên, do vậy đôi khi nó dẫn đến kết quả không như mong muốn nếu ta dùng cả hai cách.

Python 3 cho phép bạn chỉ định những tham số chỉ được truyền qua đối số có tên bằng cách sử dụng một dấu sao *.

Trong ví dụ sau, * đặt trước startend có nghĩa là tham số startend phải được truyền qua đối số có tên, nếu không muốn sử dụng giá trị mặc định của chúng.

>>> def print_data(data, *, start=0, end=100):
    for value in (data[start:end]):
        print(value)
>>> data = ['a', 'b', 'c', 'd', 'e', 'f']
>>> print_data(data)
a
b
c
d
e
f
>>> print_data(data, start=4)
e
f
>>> print_data(data, end=2)
a
b

Đối số thay đổi và đối số bất biến

Nhớ rằng, nếu bạn gán cùng một danh sách cho nhiều biến, bạn có thể thay đổi nó bằng bất cứ biến nào. Nhưng bạn không thể làm như vậy nếu các biến cùng trỏ đến một số hoặc một chuỗi. Đó là bởi vì các danh sách có thể thay đổi còn số và chuỗi là bất biến.

Tương tự, khi bạn truyền đối số tới hàm. Nếu đối số có thể thay đổi, giá trị của nó có thể bị thay đổi từ bên trong hàm qua các tham số tương ứng.

>>> outside = ['one', 'fine', 'day']
>>> def mangle(arg):
        arg[1] = 'terrible!'
>>> outside
['one', 'fine', 'day']
>>> mangle(outside)
>>> outside
['one', 'terrible!', 'day']

Bạn nên tránh làm thay đổi đối số truyền vào hàm. Với đối số bất biến, bạn không cần quá lo lắng, nhưng với đối số có thể thay đổi, bạn nên tạo ra một bản sao của giá trị của đối số và chỉ thực hiện các thay đổi trên bản sao này.

Leave a Reply