Daily Archives: 29/01/2016

Python – Các hàm kiểm tra trạng thái

Trong phần này chúng ta sẽ tìm hiểu một số hàm kiểm tra trạng thái.

Trong Python mọi thứ đều là đối tượng. Một đối tượng thì có các thuộc tính và phương thức. Chúng ta có thể dùng các hàm kiểm tra để biết các thuộc tính và phương thức của một đối tượng.

Hàm dir()

Hàm dir() trả về một danh sách các thuộc tính và phương thức có trong một đối tượng.

>>> dir(())
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index']

Trong ví dụ trên chúng ta dùng hàm dir() để xem các thuộc tính và phương thức có trong một tuple.

>>> print (().__doc__)
tuple() -> empty tuple
tuple(iterable) -> tuple initialized from iterable's items

If the argument is a tuple, the return value is the same object.

Trong đó có thuộc tính __doc__, chúng ta có thể dùng hàm print() để xem nội dung của thuộc tính này.

import sys

class Object:
   def __init__(self):
      pass
   def examine(self):
      print (self)


o = Object()

print (dir(o))
print (dir([]))
print (dir({}))
print (dir(1))
print (dir())
print (dir(len))
print (dir(sys))
print (dir("String"))

Trong đoạn code trên chúng ta kiểm tra một vài đối tượng khác nhau trong đó có cả đối tượng thuộc lớp do chúng ta định nghĩa.

Nếu không đưa tham số vào thì hàm dir() trả về tên các module trong chương trình.

>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']
>>> import sys
>>> import math, os
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'math', 'os', 'sys']

Hàm type() và id()

Hàm type() trả về kiểu dữ liệu của một đối tượng.

import sys

def function(): pass

class MyObject():
   def __init__(self):
      pass

o = MyObject()

print (type(1))
print (type(""))
print (type([]))
print (type({}))
print (type(()))
print (type(object))
print (type(function))
print (type(MyObject))
print (type(o))
print (type(sys))

Trong ví dụ trên chúng ta in ra kiểu dữ liệu của một số đối tượng.

<type 'int'>
<type 'str'>
<type 'list'>
<type 'dict'>
<type 'tuple'>
<type 'type'>
<type 'function'>
<type 'classobj'>
<type 'instance'>
<type 'module'>

Hàm id() sẽ trả về id của một đối tượng.

import sys

def fun(): pass

class MyObject():
   def __init__(self):
      pass

o = MyObject()

print (id(1))
print (id(""))
print (id({}))
print (id([]))
print (id(sys))
print (id(fun))
print (id(MyObject))
print (id(o))
print (id(object))

Trong ví dụ trên chúng ta in ra id của một số đối tượng, kể cả đối tượng có sẵn trong Python.

135717024
3084304536
3084111220
3084104940
3084304500
3084112812
3084074556
3084130444
135568640

Module sys

Module sys cung cấp các thông tin về hệ điều hành và tình trạng của Python.

>>> import sys
>>> sys.version
'3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)]'
>>> sys.platform
'win32'
>>> sys.path
['C:\\Users\\PhoCode\\', 'C:\\python\\python35.zip', 'C:\\python\\DLLs', 'C:\\python\\lib', 'C:\\python', 'C:\\python\\lib\\site-packages']

Trong ví dụ trên chúng ta kiểm tra phiên bản Python, tên hệ điều hành và các đường dẫn thư mục liên quan.

Chúng ta sẽ kiểm tra một số thuộc tính khác trong module sys. Bạn cũng có thể dùng hàm dir() để lấy về danh sách đầy đủ các thuộc tính và phương thức có trong module sys.

>>> sys.maxsize
2147483647
>>> sys.executable
'C:\\python\\python.exe'
>>> sys.argv
['']

Trong ví dụ trên chúng ta kiểm tra 3 thuộc tính là maxsize, executable, và argv.

>>> sys.maxsize
2147483647

Thuộc tính maxsize lưu trữ giá trị số nguyên lớn nhất được hỗ trợ trong Python.

>>> sys.executable
'C:\\python\\python.exe'

Thuộc tính executable trả về đường dẫn đầy đủ đến trình thông dịch Python.

>>> sys.argv
['']

Thuộc tính argv trả về tham số được đưa vào trình thông dịch Python. Ở đây là danh sách rỗng, nếu bạn chạy từ file script thì tham số này sẽ có ít nhất đường dẫn đến file script của bạn.

Một số hàm khác

def fun(): pass

print (hasattr(object, '__doc__'))
print (hasattr(fun, '__doc__'))
print (hasattr(fun, '__call__'))

print (getattr(object, '__doc__'))
print (getattr(fun, '__doc__'))

Hàm hasattr() kiểm tra xem một đối tượng nào đó có chứa thuộc tính nào đó không. Hàm getattr() sẽ trả về nội dung của thuộc tính đó nếu có.

True
True
True
The most base type
None

Hàm isinstance kiểm tra xem một đối tượng có thuộc một lớp nào đó không.

class MyObject():
   def __init__(self):
      pass

o = MyObject()

print (isinstance(o, MyObject))
print (isinstance(o, object))
print (isinstance(2, int))
print (isinstance('str', str))

Chúng ta đã biết là tất cả mọi thứ trong Python đều là đối tượng, kể cả số hay chuỗi. Tất cả các lớp trong Python đều kế thừa từ một lớp cơ sở có tên là object. Do đó hàm instance sẽ trả về true nếu chúng ta kiểm tra xem một đối tượng có thuộc lớp object hay không.

True
True
True
True

Hàm issubclass() kiểm tra xem một lớp có được kế thừa từ một lớp khác hay không. Nếu chúng ta dùng hàm issubclass() với 2 tham số giống nhau thì cũng trả về True, nói cách khác trong Python thì một lớp cũng là lớp con của chính nó.

class Object():
   def __init__(self):
      pass

class Wall(Object):
   def __init__(self):
      pass

print (issubclass(Object, Object))
print (issubclass(Object, Wall))
print (issubclass(Wall, Object))
print (issubclass(Wall, Wall))

Trong ví dụ trên, chúng ta định nghĩa lớp Wall kế thừa từ lớp Object.

True
False
True
True

Thuộc tính __doc__ là đoạn chuỗi mô tả về một đối tượng nào đó. Thuộc tính __name__ là chuỗi chứa tên của một đối tượng nào đó.

def noaction():
   '''Blah blah blah'''
   pass


funcs = [noaction, len, str]

for i in funcs:
   print (i.__name__)
   print (i.__doc__)
   print ("-" * 75)

Trong ví dụ trên, chúng ta tạo một list chứa tên các hàm, trong đó có một hàm do chúng ta tự định nghĩa, 2 hàm còn lại có sẵn. Sau đó chúng ta duyệt qua list này và in nội dung hai thuộc tính __name____doc__ của mỗi hàm ra.

noaction
Blah blah blah
---------------------------------------------------------------------------
len
len(object) -> integer

Return the number of items of a sequence or mapping.
---------------------------------------------------------------------------
str
str(object) -> string

Return a nice string representation of the object.
If the argument is a string, the return value is the same object.
---------------------------------------------------------------------------

Cuối cùng là hàm callable(), hàm này kiểm tra xem một đối tượng có phải là một hàm hay không.

class Car:      
    def setName(self, name):
        self.name = name    

def fun():
    pass

c = Car()    
    
print (callable(fun))
print (callable(c.setName))
print (callable([]))
print (callable(1))

Trong ví dụ trên chúng ta kiểm tra 4 đối tượng fun, c.setName, [] (list rỗng) và số 1 có phải là các hàm hay không.

print (callable(fun))
print (callable(c.setName))

Chắc chắn fun chính là một hàm do chúng ta tự định nghĩa, setName() là một phương thức (hàm nằm trong một lớp) của lớp Car.

True
True
False
False

 

Python – Iterator và Generator

Trong phần này chúng ta sẽ tìm hiểu về Iterator và Generator.

Iterator là một khái niệm chỉ các lớp dạng danh sách cho phép chúng ta duyệt qua chúng mà không cần quan tâm đến kiểu dữ liệu đó là gì.

Để một lớp được gọi là Iterator thì lớp đó phải hỗ trợ 2 phương thức là __iter__()__next__(). Cả 2 phương thức đó được gọi chung là giao thức iterator (Iterator Protocol). Trong đó phương thức __iter__() chỉ đơn giản là trả về đối tượng tham chiếu tới chính nó, còn phương thức __next__() trả về phần tử tiếp theo có trong danh sách cho đến phần tử cuối cùng thì giải phóng exception StopIteration.

Hầu hết các kiểu dữ liệu dạng danh sách trong Python như kiểu list, tuple, string, dictionary đều hỗ trợ giao thức Iterator.

str = "formidable"
it = iter(str)

print (it.__next__())
print (it.__next__())
print (it.__next__())

print (list(it))

Trong đoạn code trên, chúng ta sử dụng tính tăng iterator của một string. Hàm iter() sẽ gọi đến phương thức __iter__() có sẵn trong lớp string để lấy về đối tượng tham chiếu đến chính nó. Phương thức __next__() sẽ trả về phần tử tiếp theo trong danh sách. Ngoài ra bạn còn có thể dùng các hàm như list(), tuple() để tạo các đối tượng danh sách mới dựa trên một iterator.

f
o
r
['m', 'i', 'd', 'a', 'b', 'l', 'e']

Trong ví dụ dưới đây, chúng ta sẽ viết một lớp riêng có hỗ trợ giao thức Iterator.

class seq:
   def __init__(self):
      self.x = 0

   def __next__(self):
      self.x += 1
      return self.x**self.x

   def __iter__(self):
      return self


s = seq()
n = 0

for i in s:
   print (i)
   n += 1
   if n > 10:
      break

Trong đoạn code trên, chúng ta tạo một dãy số dài vô tận. Khi chúng ta sử dụng vòng lặp for, vòng lặp sẽ tự động gọi hàm iter() lên đối tượng của chúng ta để lấy về một đối tượng tham chiếu tới chính nó, và cứ mỗi lần lặp nó lại gọi đến phương thức __next__() đế lấy phần tử tiếp theo trong dãy.

def __next__(self):
    self.x += 1
    return self.x**self.x

Phương thức __next__() trả về phần tử tiếp theo trong dãy.

 def __iter__(self):
    return self

Phương thức __iter__ trả về đối tượng tham chiếu đến chính nó (hay còn gọi là đối tượng iterator).

 if n > 10:
      break

Do chúng ta tạo một danh sách dài vô tận nên phải có câu lệnh để ngắt vòng lặp.

1
4
27
256
3125
46656
823543
16777216
387420489
10000000000
285311670611

Trong ví dụ dưới đây, chúng ta sẽ ngắt vòng lặp bằng cách giải phóng exception StopIteration.

class seq14:
   def __init__(self):
      self.x = 0

   def __next__(self):
      self.x += 1
      if self.x > 14:
         raise StopIteration
      return self.x**self.x

   def __iter__(self):
      return self


s = seq14()

for i in s:
   print (i)

Trong đoạn code trên chúng ta in ra 14 số đầu tiên trong dãy.

if self.x > 14:
    raise StopIteration

StopIteration sẽ ngắt vòng lặp.

Generator

Generator chẳng qua cũng chỉ là Iterator, chúng cũng tạo ra một đối tượng kiểu danh sách, nhưng bạn chỉ có thể duyệt qua các phần tử của generator một lần duy nhất vì generator không lưu dữ liệu trong bộ nhớ mà cứ mỗi lần lặp thì chúng sẽ tạo phần tử tiếp theo trong dãy và trả về phần tử đó.

Đoạn code dưới đây ví dụ cách định nghĩa một generator.

def gen():
  x, y = 1, 2
  yield x, y
  x += 1
  yield x, y


it = gen()

print (it.__next__())
print (it.__next__())

try:
  print (it.__next__())
except StopIteration:
  print ("Iteration finished")

Để định nghĩa một generator thì chúng ta dùng từ khóa def giống như khi định nghĩa một hàm. Trong generator chúng ta dùng từ khóa yield để trả về các phần tử.

Từ khóa yield có chức năng y hệt như từ khóa return, chỉ khác ở chỗ là khi bạn gọi phương thức __next__() lần thứ nhất thì generator thực hiện các công việc tính toán giá trị rồi gặp từ khóa yield nào thì nó sẽ trả về các phần tử tại ví trí đó, khi bạn gọi phương thức __next__() lần thứ hai thì generator không bắt đầu chạy tại vị trí đầu tiên mà bắt đầu ngay phía sau từ khóa yield thứ nhất. Cứ như thế generator tạo ra các phần tử trong dãy, cho đến khi không còn gặp từ khóa yield nào nữa thì giải phóng exception StopIteration.

(1, 2)
(2, 2)
Iteration finished