Hướng dẫn python docstring kwargs

What is the conventional way to express in a docstring the expected types of keyword arguments?

Or is this out of principle something I should not be doing at all? Google is suspiciously un-forthcoming on the subject.

(I am interested in doing this because I find that keeping track of expected types of all variables is very helpful as I code. I use PyCharm, which warns me when arguments have unexpected types, or when custom class attributes may be unresolved etc.)

However, I am finding that sometimes the list of possible keywrord arguments listed as

def foo(bar, parameter_1: int=1, paramter_2: str='', ...etc )

can become long and unreadable..

Consider the following code

class Person:
    def __init__(self, id: int, **kwargs):
        """
        :param id: social security number
        :type id: int
        :param **name: person's first name
        :type **name: str
        :param **age: person's age in years, rounded down
        :type **age: int
        """
        self.data = kwargs


bob = Person(123456568, name='Bob', age=48)
sally = Person(1245654568, name='Sally', age='22')

I would like to use the docstring to state the expected types. And I would like PyCharm to warn me that sally's age has the wrong type. Of course, I don't know if PyCharm even has the capacity to 'understand' that level of detail in the docstring. Nevertheless I'd like to know what the conventional way to do it is.

Other comments and suggestions about kwargs and docstrings also welcome.

There is a doctstring example for Sphinx in their documentation. Specifically they show the following:

def public_fn_with_googley_docstring(name, state=None):
"""This function does something.

Args:
   name (str):  The name to use.

Kwargs:
   state (bool): Current state to be in.

Returns:
   int.  The return code::

      0 -- Success!
      1 -- No good.
      2 -- Try again.

Raises:
   AttributeError, KeyError

A really great idea.  A way you might use me is

>>> print public_fn_with_googley_docstring(name='foo', state=None)
0

BTW, this always returns 0.  **NEVER** use with :class:`MyPublicClass`.

"""
return 0

Though you asked about sphinx explicitly, I would also point to the Google Python Style Guide. Their docstring example seems to imply that they don't call out kwargs specifically. (other_silly_variable=None)

def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
"""Fetches rows from a Bigtable.

Retrieves rows pertaining to the given keys from the Table instance
represented by big_table.  Silly things may happen if
other_silly_variable is not None.

Args:
    big_table: An open Bigtable Table instance.
    keys: A sequence of strings representing the key of each table row
        to fetch.
    other_silly_variable: Another optional variable, that has a much
        longer name than the other args, and which does nothing.

Returns:
    A dict mapping keys to the corresponding table row data
    fetched. Each row is represented as a tuple of strings. For
    example:

    {'Serak': ('Rigel VII', 'Preparer'),
     'Zim': ('Irk', 'Invader'),
     'Lrrr': ('Omicron Persei 8', 'Emperor')}

    If a key from the keys argument is missing from the dictionary,
    then that row was not found in the table.

Raises:
    IOError: An error occurred accessing the bigtable.Table object.
"""
pass

A-B-B has a question about the accepted answer of referencing the subprocess management documentation. If you import a module, you can quickly see the module docstrings via inspect.getsource.

An example from the python interpreter using Silent Ghost's recommendation:

>>> import subprocess
>>> import inspect
>>> import print inspect.getsource(subprocess)

Of course you can also view the module documentation via help function. For example help(subprocess)

I'm not personally a fan of the subprocess docstring for kwargs as an example, but like the Google example it doesn't list kwargs seperately as shown in the Sphinx documentation example.

def call(*popenargs, **kwargs):
"""Run command with arguments.  Wait for command to complete, then
return the returncode attribute.

The arguments are the same as for the Popen constructor.  Example:

retcode = call(["ls", "-l"])
"""
return Popen(*popenargs, **kwargs).wait()

I'm including this answer to A-B-B's question because it's worth noting that you can review any module's source or documentation this way for insights and inspiration for commenting your code.

  • 1. *args trong Python
  • 2. **kwargs  trong Python
  • 3. Sử dụng *args và **kwargs để gọi đến một hàm
  • 4. Sử dụng *args và **kwargs trên cùng một dòng để gọi đến một hàm

Nội dung chính

  • 2. **kwargs  trong Python
  • 3. Sử dụng *args và **kwargs để gọi đến một hàm
  • 4. Sử dụng *args và **kwargs trên cùng một dòng để gọi đến một hàm

Đối với ngôn ngữ lập trình Python, cú pháp đặc biệt *args trong phần định nghĩa hàm (function definitions) được sử dụng dể truyền một số lượng có thể thay đổi được các đối số cho một hàm. Nó được sử dụng để truyền vào cho hàm một danh sách đối số có số lượng đối số thay đổi được và các đối số đều là non-keyworded, tức là chúng đều chưa được định từ khóa.

– Ý nghĩa: Cú pháp *args, ký hiệu * tức là nhận vào một số lượng có thể thay đổi được các args – viết tắt của argument (đối số truyền vào cho hàm).

– *args cho phép hàm của bạn có thể nhận vào nhiều đối số hơn số lượng đối số chính thức đã được chỉ định trước đó. Với *args, bất kỳ số lượng đối số bổ sung nào cũng có thể được truyền vào hàm thông qua các tham số chính thức hiện tại của hàm (kể cả bạn không truyền thêm đối số bổ sung nào vào cũng được).

– Ví dụ: Chúng ta muốn tạo ra một hàm thực hiện phép nhân, nhận vào số lượng đối số tùy ý và nhân chúng lại với nhau. Việc này có thể được thực hiện nhờ vào *args.

– Sử dụng ký hiệu *, thì cái biến mà chúng ta gắn nó với ký hiệu * sẽ trở thành một kiểu dữ liệu iterable, nghĩa là lúc này bạn có thể thực hiện những việc như lặp/duyệt qua biến đó, hay sử dụng các hàm bậc cao hơn chẳng hạn như map và filter, v.v… để xử lý/thao tác nó. (giải thích thêm, trong Python, iterable có nghĩa là gì? iterable là bất kỳ đối tượng nào ở trong Python mà có khả năng trả về từng thành viên của nó, bản thân nó có thể được lặp/duyệt thông qua một vòng lặp for. Một số ví dụ quen thuộc về iterable thường gặp là list, tuple, và string – đây đều là các kiểu dữ liệu nối tiếp có thể được lặp thông qua một vòng lặp for).

– Ví dụ sử dụng *arg trong Python:

+ Ví dụ 1:

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: https://www.facebook.com/cafedevn
#Instagram: https://instagram.com/cafedevn
#Twitter: https://twitter.com/CafedeVn
#Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


# Python program to illustrate   
# *args for variable number of arguments 
def myFun(*argv):  
    for arg in argv:  
        print (arg) 
    
myFun('Hello', 'Welcome', 'to', 'cafedevn')  

Kết quả in ra là:

Hello
Welcome
to
cafedevn

+ Ví dụ 2:

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: https://www.facebook.com/cafedevn
#Instagram: https://instagram.com/cafedevn
#Twitter: https://twitter.com/CafedeVn
#Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


# Python program to illustrate  
# *args with first extra argument 
def myFun(arg1, *argv): 
    print ("First argument :", arg1) 
    for arg in argv: 
        print("Next argument through *argv :", arg) 
  
myFun('Hello', 'Welcome', 'to', 'cafedev.vn') 

Kết quả in ra là:

First argument : Hello
Next argument through *argv : Welcome
Next argument through *argv : to
Next argument through *argv : cafedev.vn

2. **kwargs  trong Python

Đối với ngôn ngữ lập trình Python, cú pháp đặc biệt **kwargs trong phần định nghĩa hàm (function definitions) được sử dụng dể truyền một số lượng có thể thay đổi được các đối số đã được keyworded – định từ khóa cho một hàm. **kwargs được tạo thành bởi kwargs đi kèm với hai dấu sao ở phía trước. Lý do phải sử dụng ** là bởi vì hai dấu sao cho phép chúng ta có thể truyền dữ liệu vào hàm thông qua các đối số từ khóa – keyword arguments, với số lượng tùy ý.

– Một đối số từ khóa – keyword argument là nơi mà bạn cung cấp một cái tên cho biến mà bạn đang định truyền vào trong hàm.

– Có thể tư duy theo cách sau: kwargs là một kiểu dictionary sẽ ánh xạ (maps) từng keyword (từ khóa) tới mỗi giá trị mà chúng ta truyền vào trong hàm. Đó là lý do vì sao khi chúng ta lặp qua kwargs và in ra các phần tử thì chúng được in ra không theo thứ tự nào cả.

– Ví dụ 1. Sử dụng **kwargs để truyền các đối số từ khóa với số lượng tùy ý:

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: https://www.facebook.com/cafedevn
#Instagram: https://instagram.com/cafedevn
#Twitter: https://twitter.com/CafedeVn
#Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


# Python program to illustrate   
# *kargs for variable number of keyword arguments 
  
def myFun(**kwargs):  
    for key, value in kwargs.items(): 
        print ("%s == %s" %(key, value)) 
  
# Driver code 
myFun(first ='caffedev', mid ='for', last='caffedev.vn')

Kết quả in ra là:

last == caffedev.vn
mid == for
first == caffedev

– Ví dụ 2. Sử dụng **kwargs để truyền các đối số từ khóa với số lượng tùy ý, cùng với một đối số bổ sung nữa:


# Python program to illustrate  **kargs for  
# variable number of keyword arguments with 
# one extra argument. 
  
def myFun(arg1, **kwargs):  
    for key, value in kwargs.items(): 
        print ("%s == %s" %(key, value)) 
  
# Driver code 
myFun("Hi", first ='cafedev.vn', mid ='for', last='cafedev.vn')     

Kết quả in ra là:

last == cafedev.vn
mid == for
first == cafedev.vn

3. Sử dụng *args và **kwargs để gọi đến một hàm

Ví dụ:

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: https://www.facebook.com/cafedevn
#Instagram: https://instagram.com/cafedevn
#Twitter: https://twitter.com/CafedeVn
#Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


def myFun(arg1, arg2, arg3): 
    print("arg1:", arg1) 
    print("arg2:", arg2) 
    print("arg3:", arg3) 
      
# Now we can use *args or **kwargs to 
# pass arguments to this function :  
args = ("cafedevn", "for", "cafedevn") 
myFun(*args) 
  
kwargs = {"arg1" : "cafedevn", "arg2" : "for", "arg3" : "cafedevn"} 
myFun(**kwargs) 

Kết quả in ra là:

arg1: cafedevn
arg2: for
arg3: cafedevn
arg1: cafedevn
arg2: for
arg3: cafedevn

4. Sử dụng *args và **kwargs trên cùng một dòng để gọi đến một hàm

Ví dụ:


def myFun(*args,**kwargs): 
    print("args: ", args) 
    print("kwargs: ", kwargs) 
  
  
# Now we can use both *args ,**kwargs to pass arguments to this function : 
myFun('cafedevn','for','cafedevn',first="cafedevn",mid="for",last="cafedevn") 

Kết quả in ra là:

args: ('cafedevn', 'for', 'cafedevn')
kwargs {'first': 'cafedevn', 'mid': 'for', 'last': 'cafedevn'}

Nguồn và Tài liệu tiếng anh tham khảo:

  • w3school
  • python.org
  • geeksforgeeks

Tài liệu từ cafedev:

  • Full series tự học Python từ cơ bản tới nâng cao tại đây nha.
  • Ebook về python tại đây.
  • Các series tự học lập trình khác

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

  • Group Facebook
  • Fanpage
  • Youtube
  • Instagram
  • Twitter
  • Linkedin
  • Pinterest
  • Trang chủ

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!