Ngôn ngữ Python ( ChươngIII. Hàm trong Python)

III. Hàm trong Python

1. Xây dng hàm:

Cú pháp một hàm trong Python như sau:

def tên_hàm (tham_biến_1, tham_biến_2, ...)
   # lệnh ...
   return giá_trị_hàm
 
            Trong đó def là từ khóa bắt buộc dùng để khai bó hàm, tiếp theo là tên hàm được đặt theo qui cách đặt tên, và cuối cùng là danh sách đối số(nếu có).
Ví dụ:
 
def binhPhuong(x):
   return x*x
 
        Câu lệnh đầu tiên ngay sau khai báo tên hàm có thể là một chuỗi kí tự tùy ý (string), được dùng để chỉ dẫn cho hàm (thường gọi là docstring). 
        Các tham số không cần xác định kiểu , Python sẽ tự xác định chúng dựa theo giá trị được truyền khi gọi hàm.
        Python không phân biệt truyền tham biến hay truyền tham trị khi mà bản thân biến cũng không cần khai báo.
        Ví dụ một hàm tính số Fibonacy
                        def fib(n):
print 'n =', n
if n > 1:
return n * fib(n − 1)
else:
print 'end of the line'
return 1
hoặc như sau: ( in ra dãy Fibonacci đến một giới hạn tùy ý)
>>> def fib(n):    # write Fibonacci series up to n
...     """Print a Fibonacci series up to n."""
...     a, b = 0, 1
...     while b < n:
...         print b,
...         a, b = b, a+b
... 
>>> # Now call the function we just defined:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

        Do không có khai báo rõ ràng đầu hàm và kết thúc hàm ,chỉ có dấu : nên quy cách viết hàm phải khá phụ thuộc vào cách thụt vào của các lệnh.
        Ở đây def fib(n): là khai báo hàm , nó phải được thụt vào it  nhất , các lệnh tiếp theo nằm bên trong hàm do vậy phải thụt vào nhiều hơn, các lệnh print , if , else ngang hàng nhau, lệnh  return n*fib(n-1) phải thụt vào nhiều hơn if...
        Một định nghĩa hàm đưa tên hàm đó vào một bảng ký hiệu hiện thời. Giá trị của tên hàm là một kiểu mà được thông dịch như là một hàm người sử dụng định nghĩa. Giá trị đó có thể được đăng kí một tên khác mà nó cũng có thể sử dụng như một hàm. Điều này như là một thao tác đổi tên hàm:
 
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89
            
            Khi hàm không xác định kiểu giá trị trả về (giống như hàm fib ở trên) nó ngầm định trả về một giá trị gọi là None. Ta có thể xem giá trị này như sau:
 
>>> print fib(0)
None
            
        Cũng rất đơn giản để viết một hàm trả về một danh sách chuỗi Fibonacci thay vì in ra nó:

>>> def fib2(n): # return Fibonacci series up to n
...     """Return a list containing the Fibonacci series up to n."""
...     result = []
...     a, b = 0, 1
...     while b < n:
...         result.append(b)    # see below
...         a, b = b, a+b
...     return result
... 
>>> f100 = fib2(100)    # call it
>>> f100                # write the result
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
 
2. Các kĩ thuật mở rộng với hàm:
2.1. Chng hàm
        Do không xác định cụ thể kiểu của các tham số khi khai báo hàm nên Python không có cách nào phân biệt các hàm chồng tên do vậy nó không hỗ trợ chế chồng hàm.
2.2. Hàm vi tham s có giá tr mc đnh
        Python hỗ trợ hàm với tham số có giá trị mặc định , các tham số có thể truyền theo bất kỳ thứ tự nào nếu chỉ ra tên của tham số.
        Xét 1 hàm sau
                def info(object, spacing=10, collapse=1):  
        Khi ấy lời gọi hàm có thể là :
        info(odbchelper) 
--> object=odbchelper , spacing=10 , collapse=1    
    info(odbchelper, 12)
--> object=odbchelper , spacing=12 , collapse=1    
    info(odbchelper, collapse=0)
--> object=odbchelper , spacing=10 , collapse=0 
    info(spacing=15, object=odbchelper)
--> object=odbchelper , spacing=15, collapse =1;

Việc sử dụng tham số có giá trị mặc định trước giúp thuận tiện khi gọi hàm sẽ có ít tham số cần truyền hơn định nghĩa cho phép. Xét một ví dụ khác:
 
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
    while True:
        ok = raw_input(prompt)
        if ok in ('y', 'ye', 'yes'): return True
        if ok in ('n', 'no', 'nop', 'nope'): return False
        retries = retries - 1
        if retries < 0: raise IOError, 'refusenik user'
        print complaint
Có thể gọi hàm này theo các cách như sau:
    ask_ok('Do you really want to quit?')
hoặc:
           ask_ok('OK to overwrite the file?', 2).
          Giá trị mặc định sẽ được tính tại thời điểm kết thúc định nghĩa hàm trong phạm vi định nghĩa đó. Vì thế:
i = 5
 
def f(arg=i):
    print arg
 
i = 6
f()
sẽ in ra giá trị 5 chứ không phải là 6.
Chú ý quan trọng: Giá trị mặc định chỉ được tính một lần. Điều này tạo ra một sự chênh lệch khi giá trị mặc định là một đối tượng khả biến như list, dictionary, hoặc instance của hầu hết các lớp. Ví dụ, hàm dưới đây thực hiện tích lũy các đối số cho các lời gọi tiếp theo:
def f(a, L=[]):
    L.append(a)
    return L
 
print f(1)
print f(2)
print f(3)
Nó sẽ in ra:
[1]
[1, 2]
[1, 2, 3]
            
            Nếu muốn giá trị mặc định được chia sẻ giữa các lời gọi tiếp sau, thì có thể viết lại hàm như sau:
 
def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L
2.3. Các đối số từ khóa:
          Các hàm cũng có thể được gọi sử dụng các đối số từ khóa có dạng “keyword = value”. Ví dụ, hàm sau đây:
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print "-- This parrot wouldn't", action,
    print "if you put", voltage, "volts through it."
    print "-- Lovely plumage, the", type
    print "-- It's", state, "!"
hàm này có thể được gọi với bất kì cách nào như sau:
parrot(1000)
parrot(action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')
nhưng những lời gọi dưới đây lại không có giá trị:
parrot()   # required argument missing
parrot(voltage=5.0, 'dead')  
# non-keyword argument following keyword
parrot(110, voltage=220)    
# duplicate value for argument
parrot(actor='John Cleese') # unknown keyword
          Tổng  quát, một danh sách đối số phải có các đối số vị trí bất kì tiếp theo bởi các đối số từ khóa bất kì, trong đó các từ khóa phải được chọn từ các tên tham số thông thường. Không quan trọng là tên tham số thông thường có giá trị mặc định hay không. Không có đối số nào nhận được nhiều hơn một giá trị. Đây là một ví dụ:
>>> def function(a):
...     pass
... 
>>> function(0, a=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'
2.4. Hàm với danh sách đối số tùy ý:
        Python cho phép khai báo 2 đối số đặc biệt cho phép tạo ra danh sách các đối số với số lượng tùy ý. Có nghĩa là mỗi khi gọi hàm đó. Ta có thể xác định bất kì số lượng đối số nào tùy ý. Ví dụ:

def function(first,second,*remaining):
    statement1
    statement2
    ...

        Khi gọi hàm trên, phải cung cấp giá trị cho 2 đối số bắt buộc đầu tiên.Tuy nhiên với đối số thứ 3, bởi vì nó được đánh dấu với một dấu * nên bất kì đối số thật sự nào sau 2 đối số đầu tiên sẽ được cho vào một bộ và gán cho đối số còn lại.

>>> def print_tail(first,*tail):
... print tail
...
>>> print_tail(1, 5, 2, "omega")
(5, 2, 'omega')

        Nếu sử dụng ** trước một đối số thì nó sẽ gắn một từ điển chứa bất kì đối số từ khóa nòa trong các đối số truyền vào mà không phải là cho đối số bình thường. Ví dụ:

def make_dictionary(max_length = 10, **entries):
    return dict([(key, entries[key]) for i, key in enumerate(entries.keys()) if i < max_length])

        Nếu gọi hàm này với bất kì đối số từ khóa nào khác max_length, chúng sẽ được đặt vào trong một từ điển “entries”. Nếu bao gồm cả đối số truyền cho max_length, nó sẽ được gắn cho đối số thông thường max_length:

>>> make_dictionary(max_length = 2, key1 = 5, key2 = 7, key3 = 9)
{'key3': 9, 'key2': 7}
          Như vậy, các đối số đó sẽ được nằm trong một bộ. Trước các đối số có số lượng thay đổi, không có hoặc có vài đối số thông thường có thể xuất hiện. dụ:
def fprintf(file, format, *args):
     file.write(format % args)
Tuy nhiên, chức năng này ít được sử dụng  thường xuyên.
2.5. Dng lambda (Hàm inline):
        Bên cạnh việc đăng kí một giá trị trả về cho hàm với một biến, cũng có thể tạo ra các biến mà chứa các hàm. Python cung cấp từ khóa lambda để định nghĩa các hàm không được đặt tên có thể đăng kí với các biến. Ta phải đặt các đối số trước dấu hai chấm, và giá trị trả về đặt sau từ khóa lambda. Sau khi được đăng kí với một biến. thể sử dụng biến này như là một hàm với các đối số và giá trị trả về như ví dụ sau:

>>> square = lambda x: x*x
>>> square(3)
9
        Cũng có thể sử dụng các biến khác các thông số trong một lambda. Tuy nhiên chú ý rằng hàm lambda cũng sử dụng giá trị các biến trong phạm vi nó được tạo ra.

>>> prefix = "Note: "
>>> def return_lambda(prefix):
... return lambda note: prefix + note
...
>>> prefix = "re: "
>>> f = return_lambda("Attn: ")
>>> f("Carnivorous octopi")
'Attn: Carnivorous octopi'

        Chú ý rằng tất cả các hàm trong Python có thể được lưu trữ thành các biến, và thực tế các biến là đơn giản nhất.

>>> make_note = return_lambda
>>> make_note("See: ")("lambda calculus")
'See: lambda calculus'

        Đây cũng là một dạng hàm inline như trong một số ngôn ngữ khác
                Ví dụ một hàm inline khác:

                >>>f=lambda x:x*3
         >>>f(2)
         6
         >>>f(‘Hello’)
         ‘HelloHelloHello’

        Hàm inline chỉ được phép có 1 câu lệnh , lệnh đó chính là giá trị trả về của hàm.
        Với từ khóa lambda, các hàm ẩn danh nhỏ có thể được tạo ra. Ví dụở đây là một hàm mà trả về tổng của 2 đối số của nó: “lambda a, b: a+b”. Các dạng lambda có thể được sử dụng ở bất cứ nơi nào đối tượng hàm được yêu cầu. Chúng bị hạn chế bởi một biểu thức đơn. Giống như các định nghĩa hàm lồng nhau, dạng lambda có thể tham chiếu các biến từ phạm vi chứa nó.
 
>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

No comments:

Post a Comment

Bài đăng mới: