(91) 350-9520 support@omarine.org M-F: 7 AM - 7 PM; Weekends: 9 AM - 5 PM

Lập trình Web: Python: Ngôn ngữ lập trình Python, phần 3

17. Lớp

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def display(self, nl = True):
        if (nl):
            print ('Coordinates:', self.x ,self.y)
        else:
            print ('Coordinates:', self.x ,self.y, end = '; ') 

class ColoredPoint(Point):
    def __init__(self, x, y, color):
        self.color = color
        super().__init__(x, y)
    def display(self):
        super().display(False)
        print ('Color:', self.color)

p = Point(1, 2)
p.display()
p = ColoredPoint(3, 4, 'blue')
p.display()
if ( isinstance(p, ColoredPoint) ):
    print ( '\'p\' is an instance of \'ColoredPoint\'' )

Lớp ColoredPoint kế thừa lớp Point, các phương thức của nó gọi các phương thức tương ứng của lớp cơ sở. Phương thức __init__() có vai trò như hàm thiết lập trong C++. Hàm isinstance() cho biết p là đối tượng của lớp ColoredPoint. Kết quả là

Coordinates: 1 2

Coordinates: 3 4; Color: blue

'p' is an instance of 'ColoredPoint'

Phương thức lớp

@classmethod biến một phương thức thành phương thức lớp, rồi phương thức lớp có thể được gọi sử dụng tên lớp:

class Dog:

    __objs = []

    def __init__(self, name):
        self.name = name
        self.__objs.append(self)

    @classmethod
    def get_objs(cls):
        return cls.__objs

d = Dog('Fido')
e = Dog('Buddy')

dogs = Dog.get_objs()

for dog in dogs:
     print (dog.name)

Kết quả:

Fido

Buddy

18. Bộ lặp

Bộ lặp là một đối tượng biểu diễn luồng dữ liệu. Cuộc gọi lặp lại tới phương thức __next__() của bộ lặp (hoặc đưa nó tới hàm next()) trả về hạng mục kế tiếp trong luồng dữ liệu. Khi không còn dữ liệu hoặc gặp điều kiện dừng, một tín hiệu dừng lặp được phát ra. Chúng ta có thể tạo một bộ lặp như sau:

class my_iterator:

    def __init__(self, data):
        self.data = data
        self.index = -1

    def __next__(self):
        if self.index == len(self.data) - 1:
            raise StopIteration
        self.index += 1
        return self.data[self.index]

it = my_iterator( (1, 2, 3) )
print ( next(it) , next(it), next(it) )

Kết quả là

1 2 3

19. Đối tượng tuần tự

Là đối tượng hỗ trợ truy cập các phần tử sử dụng chỉ số nguyên bắt đầu từ 0 thông qua phương thức __getitem__() và định nghĩa phương thức __len__() mà trả về chiều dài tuần tự của đối tượng. Chúng ta có thể tạo một đối tượng tuần tự như sau:

class my_sequence:

    def __init__(self, data):
        self.data = data

    def __getitem__(self, index): 
        return self.data[index]

    def __len__(self):
        return len (self.data)

seq = my_sequence( (1, 2, 3) )

for item in seq:
     print (item, end = ' ')

print ()

print ( len(seq) )

Kết quả:

1 2 3

3

Xâu kí tự, danh sách, tuple, range, bytes là các kiểu tuần tự built-in. Từ điển cũng có hai phương thức __getitem__() và __len__() nhưng không thuộc kiểu tuần tự vì các phần tử được tra thông qua khóa tùy ý mà không tuần tự theo chỉ số nguyên.

20. Đối tượng có thể lặp

Đối tượng có thể lặp hay đối tượng lặp được có khả năng trả về các thành viên của nó theo lượt. Ví dụ cho đối tượng lặp được là tất cả các kiểu tuần tự, các kiểu không tuần tự như từ điển, đối tượng file, và các đối tượng của bất kỳ lớp có phương thức __iter__() mà trả về một bộ lặp hoặc có phương thức __getitem__() mà thi hành ngữ nghĩa tuần tự (không nhất thiết phải có phương thức __len__()). Đối tượng lặp được còn được tạo ra bởi bộ phát lặp. Chi tiết này nằm trong một mục bên dưới.

Gọi hàm iter() với đối số là đối tượng lặp được sẽ trả về bộ lặp cho tối tượng, từ đó lấy các phần tử của đối tượng. Thông thường hàm iter() không cần được gọi trực tiếp, vòng lặp for sẽ thực hiện cuộc gọi tự động. Chúng ta có thể tạo đối tượng lặp được và sử dụng bộ lặp đã định nghĩa ở trên như sau:

class my_iterator:

    def __init__(self, data):
        self.data = data
        self.index = -1

    def __next__(self):
        if self.index == len(self.data) - 1:
            raise StopIteration
        self.index += 1
        return self.data[self.index]

class my_iterable:

    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return my_iterator(self.data)

obj = my_iterable( (1, 2, 3) )

it = iter(obj)

print ( next (it) , next (it), next (it) )

for item in obj:
    print (item)

Khi hàm iter() được gọi cho đối tượng obj, nó gọi phương thức __iter__() của đối tượng và trả về bộ lặp của lớp my_iterator. Một lớp bộ lặp, một lớp đối tượng lặp được định nghĩa như thế là đủ để hỗ trợ vòng lặp for. Kết quả là

1 2 3

1

2

3

Ngoài vòng lặp for, đối tượng lặp được còn được sử dụng trong các thao tác khác như zip(), map(), vv.

Chúng ta tiếp tục minh họa lớp đối tượng lặp được tự định nghĩa này hỗ trợ hàm sorted():

li = sorted(obj, reverse = True)

print (li)

Kết quả:

[3, 2, 1]

Các đối tượng lặp được built-in có bộ lặp built-in tương ứng, ví dụ

li = [ 1, 2, 3 ]

it = iter(li)

print (it)

Kết quả là

<list_iterator object at 0x7f1a53d91a90>

21. Hàm iter() và đối tượng gọi được

Gọi hàm iter() cho đối tượng lặp được chỉ là một trường hợp sử dụng. Dạng đầy đủ của hàm như sau:

iter(object[, sentinel])

Khi đối số thứ hai, sentinel được chỉ ra thì object phải là đối tượng gọi được. Đối tượng gọi được là đối tượng của lớp mà định nghĩa phương thức __call__(). Phương thức này được sử dụng khi đối tượng được gọi như một hàm. Ví dụ gọi obj(arg) là tương đương với obj.__call__(arg).

Bộ lặp được tạo trong trường hợp này sẽ gọi đối tượng không đối số cho mỗi cuộc gọi tới phương thức __next__() của nó. Nếu giá trị trả về bằng sentinel, tín hiệu dừng lặp được phát.

Sử dụng bộ lặp có “lính canh” như thế là hữu ích vì vòng lặp sẽ dừng tại điểm mong muốn thay vì đi hết dữ liệu. Chúng ta có thể tạo đối tượng gọi được như sau:

class my_callable:

    def __init__(self, data):
        self.data = data
        self.reset()

    def reset(self):
        self.index = -1

    def __call__(self):
        if self.index == len(self.data) - 1:
            self.reset()
            raise StopIteration
        self.index += 1
        return self.data[self.index]

obj = my_callable('abcd')

for ch in iter(obj, 'c'):
    print (ch)

Vòng lặp for lần lượt in các kí tự của đối tượng, và dừng khi gặp kí tự 'c'. Kết quả là

a

b

22. Đối tượng lặp được tự hành

Một lớp đối tượng lặp được có thể định nghĩa chức năng bộ lặp và cho phương thức __iter__() trả về chính nó. Đối tượng lặp được như thế đồng thời là bộ lặp, có khả năng tự hành.

class self_iterable:

    def __init__(self, data):
        self.data = data
        self.__reset()

    def __iter__(self):
        return self

    def __reset(self):
        self.index = -1

    def __next__(self):
        if self.index == len(self.data) - 1:
            self.__reset()
            raise StopIteration
        self.index += 1
        return self.data[self.index]

it = self_iterable( (1, 2, 3) )

print ( next(it) )

obj = self_iterable( (1, 2, 3) )

for num in obj:
    print (num, end = ' ')

print ()

Kết quả:

1

1 2 3

23. Bộ phát lặp

Bộ phát lặp là một hàm được định nghĩa để tạo ra đối tượng lặp được tự hành một cách tự động nhờ lệnh yield. Bộ phát lặp đơn giản hơn bộ lặp trên cơ sở lớp và là một công cụ mạnh vì luồng dữ liệu được bổ sung bất kỳ khi nào lệnh yield sản sinh một hạng mục. Chúng ta xem ví dụ dưới đây

def gen(data, sentinel):

    for item in data:
        if item is sentinel:
            break
        else:
            yield item

for num in gen( (1, 2, 3, 40, 50), 40):
    print (num, end = ' ')

print ()

li = sorted(gen( (1, 2, 3, -1, -2), -1), key = lambda x: x % 3)

print (li)

Kết quả:

1 2 3

[3, 1, 2]

24. Hàm next()

Hàm next() có dạng đầy đủ như sau:

next(iterator[, default])

Thu được hạng mục kế tiếp từ bộ lặp iterator bằng cách gọi phương thức __next__() của nó. Nếu default được chỉ ra, nó được trả về khi bộ lặp đã cạn, nếu không tín hiệu dừng lặp được phát. Ví dụ

it = iter('abcde')

sentinel = object()

item = next(it, sentinel)

while item is not sentinel:
    print (item, end = ' ')
    item = next(it, sentinel)

print ()

Kết quả:

a b c d e

25. Hàm zip(*iterables)

Tạo đối tượng lặp được tự hành mà tổng hợp các phần tử từ mỗi đối tượng lặp được đối số. Hàm trả về bộ lặp với luồng dữ liệu của các tuple, trong đó tuple thứ i chứa đựng phần tử thứ i từ mỗi đối tượng lặp được đối số. Bộ lặp dừng lại khi đối tượng lặp được đầu vào ngắn nhất đã cạn. Với đối số 1 đối tượng lặp được đơn, hàm trả về bộ lặp các tuple một phần tử. Nếu không có đối số, nó trả về một bộ lặp rỗng.

Hàm có thể là một bộ phát lặp với mã nguồn như sau:

def zip(*iterables):
    # zip('ABCD', 'xy') --> Ax By
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem is sentinel:
                return
            result.append(elem)
        yield tuple(result)

Áp dụng:

matrix = [
        [1, 2, 3, 4],
        [5, 6, 7, 8],
        [9, 10, 11, 12],
]

print ( list(zip(*matrix)) )

Kết quả:

[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

Kết quả tương tự nếu chúng ta biểu đạt danh sách

print ( [[row[i] for row in matrix] for i in range(4)] )

Tuy nhiên, nó là một danh sách các danh sách thay vì danh sách các tuple

[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

26. Một số áp dụng các hàm built-in khác, lệnh và thư viện

Hàm map(function, iterable, …) áp dụng hai biểu thức lambda tới các mục trong hai danh sách a, b, và trong danh sách a, trả về các bộ lặp tương ứng:

a = [1, 3, 5]

b = [2, 4, 6]

print ( *map(lambda x, y: x + y, a, b) )

print ( *map(lambda x: x * x, a) )

Kết quả:

3 7 11

1 9 25

Hàm sum(iterable[, start]) tính tổng các mục trong danh sách a và start với mặc định bằng 0:

a = [1, 2, 3]

print( sum(a) )

Kết quả:

6

Tính số pi với từng độ chính xác lấy trong một range:

from math import pi

print ( [round(pi, i) for i in range(1, 6)] )

Kết quả:

[3.1, 3.14, 3.142, 3.1416, 3.14159]

Sử dụng hàm lượng giác:

from math import cos

print ( cos(60/57.3) )

Kết quả:

0.5000667970857781

Tạo một số ngẫu nhiên:

from random import random

print( random() )

Kết quả:

0.1262944686535835

Xóa hai mục từ điển với lệnh del:

d = dict(one = 1, two = 2, three = 3)

del d['one'], d['two']

print (d)

Kết quả:

{'three': 3}

Mở file và đọc cho tới khi gặp một dòng trống:

with open('test.txt', 'r') as fp:
    for line in iter(fp.readline, '\n'):
        print(line, end = '')

Viết nội dung một xâu kí tự tới file:

fp = open('test.txt', 'w')

fp.write('This is a test.\n')

fp.close()

Hàm enumerate(iterable, start=0) trả về một đối tượng liệt kê các phần tử của đối tượng lặp được iterable trong mỗi tuple cùng với một số đếm bắt đầu từ start:

seasons = ['Spring', 'Summer', 'Authumn', 'Winter']

enum = enumerate(seasons, start = 2)

print (*enum)

Kết quả:

(2, 'Spring') (3, 'Summer') (4, 'Authumn') (5, 'Winter')

Hàm filter(function, iterable) thiết lập một bộ lặp từ những phần tử của iterable mà function trả về True:

f = lambda x: x % 3 == 0 or x % 5 == 0

print( *filter(f, range(1, 11)) )

Kết quả:

3 5 6 9 10

Trình bày thư mục hiện hành:

import os

print ( os.getcwd() )

Tìm văn bản khớp biểu thức chính quy:

import re

text = '12345-1234'

m = re.search (r'(\d{5})-?\d{4}', text)

if (m):
    print ( m[0], m[1] ) 
else:
    print ( 'Not found!' )

Kết quả:

12345-1234 12345

Lấy thông tin trên Web:

import re

from urllib.request import urlopen

with urlopen('https://phamtyn.wordpress.com') as response:
    for line in response:
        line = line.decode('utf-8') 
        m = re.search (r'>.*(lambda x: x \+ 1[^<]*)<', line)
        if (m):
            print ( m[1] )

Kết quả:

lambda x: x + 1

Hiển thị ngày tháng hiện tại:

from datetime import date

print ( date.today() )

Bọc văn bản:

import textwrap

doc = """The wrap() method is just like fill() except that it returns a list of strings instead of one big string with newlines to separate the wrapped lines."""

print ( textwrap.fill(doc, width=40) )

Kết quả:

The wrap() method is just like fill()
except that it returns a list of strings
instead of one big string with newlines
to separate the wrapped lines.

Tính toán với độ chính xác yêu cầu:

from decimal import *

getcontext().prec = 36

print(Decimal(1) / Decimal(7))

Kết quả:

0.142857142857142857142857142857142857

Tìm các tệp chương trình Python trong thư mục hiện hành:

from glob import glob

print ( glob('*.py') )

27. Xử lý ngoại lệ

Lỗi chia 0:

try:
    x = 1/0
except ZeroDivisionError as err:
    print('Handling run-time error:', err)

Kết quả:

Handling run-time error: division by zero

Chỉ số vượt phạm vi:

li = [1, 2]
try:
    item = li[2]
except IndexError as err:
    print('Handling run-time error:', err)

Kết quả:

Handling run-time error: list index out of range

Ngoại lệ người sử dụng định nghĩa:

class MyException(Exception):

    def __init__(self, typename, attr):
        self.typename = typename
        self.attr = attr

def check_conditions(obj, attr):
    if not hasattr(obj, attr):
        raise MyException(obj.__class__.__name__, attr)

class MyClass:
    pass

obj = MyClass()
try:
    check_conditions(obj, 'name')
except MyException as e:
    print('Handling run-time error:', '\'' + e.typename + '\'', \
        'has no attribute', '\'' + e.attr + '\'')
else:
    name = obj.name

Hàm check_conditions() kiểm tra xem đối tượng obj có thuộc tính name không. Nếu không có, nó phát sinh ngoại lệ MyException. Nếu có, chương trình an toàn truy cập thuộc tính của đối tượng. Trong ví dụ, lớp MyClass là lớp rỗng, kết quả là

Handling run-time error: 'MyClass' has no attribute 'name'

28. Đường dẫn tìm module

Khi một module được nhập khẩu từ bên ngoài, trình thông dịch sẽ tìm module trong danh sách các thư mục đưa ra bởi biến sys.path. Biến sys.path được khởi tạo từ những vị trí sau:

  • Thư mục chứa kịch bản đầu vào, hoặc thư mục hiện hành nếu không có file được chỉ ra

  • Biến môi trường PYTHONPATH

  • Mặc định phụ thuộc cài đặt

Sau khi khởi tạo, chương trình Python có thể sửa đổi biến sys.path. Nếu muốn bổ sung danh sách thư mục trong sys.path áp dụng cho mọi chương trình, bạn thực hiện như sau:

Giả sử bạn để gói fibo trong thư mục nhà và muốn thêm thư mục đó vào danh sách, cho rằng bạn đang làm việc trong môi trường ảo $HOME/python-env/mysite, việc cần làm là tạo một tệp .pth:

cat > $HOME/python-env/mysite/lib/python3.6/site-packages/fibo.pth << EOF

$HOME

EOF

Advertisements

Gửi phản hồi

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: