Tải bản đầy đủ (.docx) (23 trang)

Những hiểu biết cơ bản về Python cho người mới bắt đầu

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (117.79 KB, 23 trang )

1. Classes
-Các Class cung cấp một phương tiện kết hợp dữ liệu và chức năng với nhau. Tạo
một Class mới sẽ tạo ra một kiểu Object mới, cho phép tạo ra các cá thể mới của
kiểu đó. Mỗi cá thể Class có thể có các thuộc tính gắn liền với nó để duy trì trạng
thái của nó.
-So với các ngôn ngữ lập trình khác, cơ chế Class của Python bổ sung các Class
với tối thiểu cú pháp và ngữ nghĩa mới . Nó là một hỗn hợp của các cơ chế Class
được tìm thấy trong C và Modula-3. Các Class Python cung cấp tất cả các tính
năng tiêu chuẩn của Lập trình hướng Object: cơ chế thừa kế Class cho phép nhiều
Class cơ sở, một Class dẫn xuất có thể ghi đè lên bất kỳ phương thức nào của
Class cơ sở hoặc các Class cơ sở của nó, và một phương thức có thể gọi phương
thức của một Class cơ sở có cùng tên. Object có thể chưa số lượng tùy ý các loại
dữ liệu. . Như là đúng đối với các mô-đun, các Class tham gia vào bản chất động
của Python: chúng được tạo ra trong thời gian chạy, và có thể được sửa đổi thêm
sau khi tạo.
Trong thuật ngữ C , các thành viên Class thông thường (bao gồm các thành viên
dữ liệu) là công khai (ngoại trừ xem dưới các Biến riêng), và tất cả các hàm thành
viên đều là ảo. . Như trong Modula-3, không có viết tắt để tham chiếu các thành
viên của Object từ các phương thức của nó: hàm phương thức được khai báo với
một đối số đầu tiên rõ ràng đại diện cho Object,được cung cấp ngầm bởi cuộc gọi.
(Thiếu thuật ngữ được chấp nhận phổ biến để nói về các lớp học, tôi sẽ thỉnh
thoảng sử dụng các thuật ngữ Smalltalk và C++.Tôi sẽ sử dụng các thuật ngữ
Modula-3, vì ngữ nghĩa hướng Object của nó gần gũi hơn với ngữ nghĩa của
Python so với C , nhưng tôi mong rằng ít người đọc đã nghe về nó.)

1.1. Vấn đề về Names và Object
Các Object có tính cá nhân và nhiều tên (trong nhiều phạm vi) có thể bị ràng buộc
vào cùng một Object. Điều này được gọi là răng cưa trong các ngôn ngữ khác.
Điều này thường không được đánh giá cao trên một cái nhìn đầu tiên tại Python,
và có thể được bỏ qua một cách an toàn khi giao dịch với các loại cơ bản không
thay đổi (số, chuỗi, bộ dữ liệu). Tuy nhiên, răng cưa có tác dụng đáng


ngạc nhiên đối với ngữ nghĩa của mã Python liên quan đến các
Object có thể thay đổi được như danh sách, từ điển và hầu hết


các loại khác. Điều này thường được sử dụng vì lợi ích của các
chương trình ,vì bí danh hoạt động như con trỏ ở một số khía
cạnh. . Ví dụ, việc truyền một Object là rẻ vì chỉ có một con trỏ
được thực thi; và nếu một hàm sửa đổi một Object được truyền
vào như một đối số, người gọi sẽ thấy sự thay đổi-điều này loại bỏ
sự cần thiết của hai cơ chế truyền tham số khác nhau như trong
pascal.

1.2. Python phạm vi và khoảng tên trống
Trước khi giới thiệu các Class, trước tiên tôi phải nói cho bạn điều gì đó về các
quy tắc phạm vi của Python. Định nghĩa Class phát một số thủ thuật gọn gàng với
không gian tên và bạn cần biết cách phạm vi và không gian hoạt động để hiểu đầy
đủ những gì đang diễn ra. Ngẫu nhiên, kiến thức về chủ đề này là hữu ích cho bất
kỳ lập trình viên Python tiên tiến nào.
chúng ta hãy bắt đầu bằng một số định nghĩa.
Một không gian tên là một ánh xạ từ các tên đến các Object. Hầu hết các không
gian tên hiện được triển khai dưới dạng từ điển Python, nhưng thường không đáng
chú ý theo bất kỳ cách nào (ngoại trừ hiệu suất )và nó có thể thay đổi trong tương
lai. . Ví dụ về các không gian tên là: tập hợp các tên dựng sẵn (chứa các hàm như
abs () và các tên ngoại lệ có sẵn); tên toàn cầu trong một mô-đun; và tên địa
phương trong lời gọi hàm. Trong một nghĩa nào đó,tập các thuộc tính của một
Object cũng tạo thành một vùng tên. Điều quan trọng cần biết về không gian tên là
hoàn toàn không có mối quan hệ giữa các tên trong các không gian tên khác nhau;
ví dụ: hai mô-đun khác nhau có thể vừa định nghĩa một hàm tối đa mà không có sự
nhầm lẫn - người dùng của các mô-đun phải đặt tiền tố nó với tên mô-đun.
Nhân tiện, tôi sử dụng thuộc tính word cho bất kỳ tên nào sau một dấu chấm - ví

dụ, trong biểu thức z.real, real là một thuộc tính của Object z. Nói đúng ra, các
tham chiếu đến các tên trong các mô-đun là các tham chiếu thuộc tính: trong biểu
thức modname.funcname, modname là một Object mô-đun và funcname là một
thuộc tính của nó. Trong trường hợp này,sẽ xảy ra một cách đơn giản.

Thuộc tính có thể là chỉ đọc hoặc có thể ghi. Trong trường hợp sau, việc gán cho
các thuộc tính là có thể. Các thuộc tính mô-đun có thể ghi được: bạn có thể viết


modname.the_answer = 42. Các thuộc tính có thể ghi cũng có thể xóa
với câu lệnh del. Ví dụ: del modname.the_answer sẽ xóa thuộc
tính the_answer khỏi Object có tên là modname.
Không gian tên được tạo ra ở những thời điểm khác nhau và có thời gian sống
khác nhau. Không gian tên chứa các tên dựng sẵn được tạo khi trình thông dịch
Python khởi động và không bao giờ bị xóa. Không gian tên chung cho mô-đun
được tạo khi định nghĩa mô-đun được đọc; thông thường, các không gian tên môđun cũng kéo dài cho đến khi thông dịch viên thoát. Các câu lệnh được thực hiện
bởi lời gọi cao cấp nhất của trình thông dịch. hoặc đọc từ một tập tin kịch bản
hoặc tương tác, được coi là một phần của một mô-đun gọi là __main__, do đó, họ
có không gian tên chung của riêng họ. (Tên được xây dựng trong thực tế cũng
sống trong một môdun,điều này gọi là builtins).
Không gian tên cục bộ cho một hàm được tạo ra khi hàm được gọi, và bị xóa khi
hàm trả về hoặc đặt ra một ngoại lệ không được xử lý trong hàm. (Trên thực tế,
quên đi sẽ là một cách tốt hơn để mô tả những gì thực sự xảy ra.) Tất nhiên,
invocations đệ quy từng có không gian tên địa phương riêng của họ.

Phạm vi là vùng văn bản của chương trình Python, trong đó không gian tên có thể
truy cập trực tiếp. "Trực tiếp truy cập" ở đây có nghĩa là một tham chiếu không đủ
tiêu chuẩn cho một tên cố gắng tìm tên cho không gian tên

Mặc dù phạm vi được xác định tĩnh, chúng được sử dụng động. Bất cứ lúc

nào trong quá trình thực hiện, có ít nhất ba phạm vi lồng nhau có các không
gian tên có thể truy cập trực tiếp:
• phạm vi bên trong nhất, được tìm kiếm đầu tiên, chứa các tên địa phương
• phạm vi của bất kỳ chức năng kèm theo nào, được tìm kiếm bắt đầu bằng
gần nhất
• phạm vi tiếp theo đến cuối cùng chứa các tên toàn cục của mô-đun hiện tại
• phạm vi ngoài cùng (tìm kiếm cuối cùng) là vùng tên chứa các tên dựng
sẵn


Nếu tên được khai báo toàn cục, thì tất cả các tham chiếu và bài tập sẽ chuyển
trực tiếp đến phạm vi giữa chứa tên toàn cầu của mô-đun. Để khôi phục lại các
biến được tìm thấy bên ngoài pham vi bên trong nhât,câu lệnh nonlocal có thể
được sử dụng. ; nếu không khai báo nonlocal, các biến đó là read-only (một nỗ lực
để ghi vào một biến như vậy sẽ chỉ tạo một biến cục bộ mới trong phạm vi bên
trong nhất, để biến không thay đổi được được đặt tên giống hệt nhau)
Thông thường, phạm vi cục bộ tham chiếu đến tên cục bộ của hàm hiện tại (văn
bản). Các hàm bên ngoài, phạm vi cục bộ tham chiếu cùng một không gian tên như
phạm vi toàn cục: không gian tên của mô dun.Các định nghĩa Class đặt một không
gian khác cho phạm vi cục bộ .
Điều quan trọng là nhận ra rằng phạm vi được xác định bằng văn bản: phạm vi
toàn cầu của hàm được định nghĩa trong mô-đun là không gian tên của mô-đun,
bất kể từ đâu hoặc bằng bí danh mà hàm được gọi. . Mặt khác, tìm kiếm thực tế tên
được thực hiện động, vào thời gian chạy - tuy nhiên, định nghĩa ngôn ngữ đang
phát triển theo độ phân giải tên tĩnh, vào thời gian "biên dịch", do đó, không dựa
vào độ phân giải tên động!(thực tế,các biến cục bộ đã được xác định tĩnh)
Một điều kỳ quặc đặc biệt của Python là - nếu không có câu lệnh chung nào có
hiệu lực - việc gán cho các tên luôn đi vào phạm vi bên trong nhất. Các bài tập
không sao chép dữ liệu - chúng chỉ liên kết các tên với các Object. Điều tương tự
cũng đúng đối với việc xóa: câu lệnh del x loại bỏ ràng buộc của x từ không gian

tên được tham chiếu bởi phạm vi cục bộ. Trên thực tế, tất cả các hoạt động giới
thiệu tên mới đều sử dụng phạm vi địa phương cụ thể, các câu lệnh
nhập khẩu và các định nghĩa hàm liên kết tên mô-đun hoặc hàm
trong phạm vi cục bộ.
.
Câu lệnh chung có thể được sử dụng để chỉ ra rằng các biến cụ thể sống trong
phạm vi toàn cầu và sẽ được hồi phục ở đó; câu lệnh nonlocal cho biết rằng các
biến cụ thể sống trong một phạm vi kèm theo và sẽ được phục hồi ở đó

1.2.1. Phạm vi và khoảng tên trống ví dụ


Đây là một ví dụ minh họa cách tham khảo các phạm vi và không gian tên khác
nhau và mức độ ảnh hưởng của biến toàn cầu và không liên quan đến biến:
def scope_test():
def do_local():
spam = "local spam"
def do_nonlocal():
nonlocal spam
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam"
spam = "test spam"
do_local()
print("After local assignment:", spam)
do_nonlocal()
print("After nonlocal assignment:", spam)
do_global()
print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)
Đầu ra của mã ví dụ là :
After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
Lưu ý cách gán địa phương (mặc định) đã không thay đổi tính ràng buộc spam của
scope_test. Nhiệm vụ không tập trung đã thay đổi ràng buộc của scope_test về
spam và nhiệm vụ toàn cục đã thay đổi ràng buộc cấp modun
Bạn cũng có thể thấy rằng không có sự ràng buộc trước đó cho spam trước khi
gán toàn cầu.
1.3. A First Look at Classes


Các Class giới thiệu một chút cú pháp mới, ba kiểu Object mới và một số ngữ
nghĩa mới.
1.3.1. Class Definition Syntax
Dạng định nghĩa Class đơn giản nhất trông giống như sau:
class ClassName:
<statement-1>
.
.
.
<statement-N>
Các định nghĩa Class, như các định nghĩa hàm (câu lệnh def) phải được thực hiện
trước khi chúng có hiệu lực. (Bạn có thể định nghĩa một cách định nghĩa Class
trong một nhánh của câu lệnh if hoặc bên trong một hàm)
Trong thực tế, các câu lệnh bên trong định nghĩa Class thường sẽ là định nghĩa
chức năng, nhưng các câu lệnh khác được cho phép và đôi khi hữu ích - chúng ta

sẽ quay lại sau này. Các định nghĩa hàm bên trong một Class thường có một dạng
danh sách đối số đặc biệt, được quyết định bởi các quy ước gọi cho các phương
thức - một lần nữa, điều này được giải thích sau
Khi một định nghĩa Class được nhập vào, một vùng tên mới được tạo ra, và được
sử dụng như là phạm vi cục bộ - do đó, tất cả các gán cho các biến cục bộ đi vào
không gian tên mới này. Đặc biệt,nghĩa của hàm liên kết tên của hàm
mới ở đây
Khi một định nghĩa Class được để lại bình thường (thông qua kết thúc), một
Object Class được tạo ra. Về cơ bản, đây là một trình bao bọc xung quanh nội
dung của không gian tên được tạo bởi định nghĩa Class;chúng ta sẽ tìm hiểu thêm
về các Object Class trong phần tiêp theo
1.3.2. Class Objects
Các Object Class hỗ trợ hai loại hoạt động: tham chiếu thuộc tính và sự thuyết
trình


Tham chiếu thuộc tính sử dụng cú pháp tiêu chuẩn được sử dụng cho tất cả các
tham chiếu thuộc tính trong Python: obj.name. Tên thuộc tính hợp lệ là tất cả các
tên trong không gian tên của Class khi Object Class được tạo.vì vậy nếu
định nghĩa Class trông như thế này:
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
sau đó MyClass.i và MyClass.f là các tham chiếu thuộc tính hợp lệ, trả về một số
nguyên và một Object hàm tương ứng. Các thuộc tính Class cũng có thể được gán
cho, vì vậy bạn có thể thay đổi giá trị Myclass.i bằng cách gán ._doc_ cũng là
thuộc tính hợp lệ ,trả về chuỗi tài liệu thuộc về Class:
Class instantiation sử dụng ký hiệu hàm. Chỉ cần giả vờ rằng Object Class là một

hàm không tham số trả về một cá thể mới của Class đó. Ví dụ (giả sử Class trên):
x = MyClass()
tạo một thể hiện mới của Class và gán Object này cho biến cục bộ x.
Thao tác khởi tạo ("gọi" một Object Class) tạo ra một Object trống. Nhiều Class
muốn tạo Object với các cá thể được tùy chỉnh theo trạng thái ban đầu cụ thể. Do
đó một Class có thể định nghĩa một phương thức đặc biệt có tên_init_(),như sau
def __init__(self):
self.data = []
Khi một Class định nghĩa một phương thức __init __ (), Class instantiation sẽ tự
động gọi __init __ () cho cá thể Class mới được tạo ra. Vì vậy, trong ví dụ này, một
thể hiện khởi tạo mới có thể thu được bằng cách:
x = MyClass()
Tất nhiên, phương thức __init __ () có thể có các đối số cho tính linh hoạt cao
hơn. Trong trường hợp đó, các đối số được đưa ra cho toán tử instantiation class
được truyền vào __init __ (). Ví dụ,


>>>
>>> class Complex:
...
def __init__(self, realpart, imagpart):
...
self.r = realpart
...
self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)
1.3.3. Instance Objects

Bây giờ chúng ta có thể làm gì với các Object thể hiện? Các hoạt động duy nhất
được hiểu bởi các Object instance là các tham chiếu thuộc tính. Có hai loại tên
thuộc tính hợp lệ, thuộc tính hợp lệ ,thuộc tính dữ liệu và phương thức
các thuộc tính dữ liệu tương ứng với "các biến cá thể" trong Smalltalk và "các
thành viên dữ liệu" trong C . Các thuộc tính dữ liệu không cần khai báo; giống
như các biến cục bộ, chúng xuất hiện khi chúng được gắn cho, Ví dụ, nếu x là cá
thể của MyClass được tạo ở trên, đoạn mã sau đây sẽ in giá trị 16, mà không để
lại dấu vết:
x.counter = 1
while x.counter < 10:
x.counter = x.counter * 2
print(x.counter)
del x.counter
Loại tham chiếu thuộc tính instance khác là một phương thức. Phương thức là một
hàm "thuộc về" một Object. (Trong Python, phương thức term không phải là duy
nhất đối với các cá thể Class: các kiểu Object khác cũng có thể có các phương
thức. Ví dụ, liệt kê các Object có các phương thức được gọi là chắp thêm, chèn,
loại bỏ, sắp xếp, vv. Tuy nhiên, trong cuộc thảo luận sau, chúng tôi sẽ sử dụng
phương thức thuật ngữ độc quyền để chỉ các phương thức của các Object thể hiện
lợp,trừ khi được quy định rõ ràng khác)
Các tên phương thức hợp lệ của một Object thể hiện phụ thuộc vào Class của nó.
Theo định nghĩa, tất cả các thuộc tính của một Class là các Object hàm xác định
các phương thức tương ứng của các cá thể của nó.vì vậy,trong ví dụ của chúng tôi,
x.f là một tham chiếu phương thức hợp lệ, vì MyClass.f là một hàm, nhưng x.i thì


không, vì MyClass.i thì không. Nhưng x.f không giống với MyClass.f - nó là một
Object phương thức, không phải là một Object hàm
1.3.4. Method Objects
Thông thường, một phương thức được gọi ngay sau khi nó bị ràng buộc:

x.f()
Trong ví dụ MyClass, điều này sẽ trả về chuỗi 'hello world'. Tuy nhiên, không cần
phải gọi phương thức ngay lập tức: x.f là một Object phương thức, và có thể được
cất giữ và được gọi sau đó.ví dụ:
xf = x.f
while True:
print(xf())
sẽ tiếp tục in thế giới hello cho đến cuối thời gian.
What exactly happens when a method is called? You may have noticed that x.f()
was called without an argument above, even though the function definition for f()
specified an argument. Điều gì chính xác xảy ra khi một phương pháp được gọi
là? Bạn có thể nhận thấy rằng x.f () được gọi mà không có một đối số ở trên, mặc
dù định nghĩa hàm cho f () đã chỉ định một đối số.điều gì xày ra với đối số? ?
Chắc chắn Python đặt ra một ngoại lệ khi một hàm yêu cầu đối số được gọi mà
không cần bất kỳ - ngay cả khi đối số không thực sự được sử dụng…
Trên thực tế, bạn có thể đoán câu trả lời: điều đặc biệt về các phương thức là
Object thể hiện được chuyển như đối số đầu tiên của hàm. Trong ví dụ của chúng
ta, hàm x.f () chính xác tương đương với MyClass.f(x). Nói chung, gọi một phương
thức với một danh sách n đối số tương đương với việc gọi hàm tương ứng với một
danh sách đối số được tạo bằng cách chèn Object Object của phương thức trước
đối số đầu tiên
Nếu bạn vẫn không hiểu các phương thức hoạt động như thế nào, thì việc xem xét
triển khai có thể làm rõ các vấn đề. Khi một thuộc tính thể hiện được tham chiếu
không phải là thuộc tính dữ liệu,Class của nó được tìm kím .Nếu tên
biểu thị một Object hàm, , một Object phương thức được tạo ra
bằng cách đóng gói (con trỏ đến) Object thể hiện và Object hàm
chỉ tìm thấy cùng nhau trong một Object trừu tượng: đây là
Object phương thức.khi Object method được gọi với một danh



sách tham số, , một danh sách đối số mới được xây dựng từ
Object thể hiện và danh sách đối số, và Object hàm được gọi với
danh sách đối số mới này.

1.3.5. Class and Instance Variables
Nói chung, các biến cá thể là dữ liệu duy nhất cho mỗi biến thể hiện và Class là
cho các thuộc tính và các phương thức được chia sẻ bởi tất cả các cá thể của
Class:
class Dog:
kind = 'canine'
all instances

# class variable shared by

def __init__(self, name):
self.name = name
# instance variable unique
to each instance
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind
'canine'
>>> e.kind
'canine'
>>> d.name
'Fido'
>>> e.name
'Buddy'

# shared by all dogs

# shared by all dogs
# unique to d
# unique to e

Như đã thảo luận trong A Word Giới thiệu về Tên và Object, dữ liệu được chia sẻ
có thể có tác dụng đáng ngạc nhiên với các Object có thể thay đổi như danh sách
và từ điển. . Ví dụ, danh sách các thủ thuật trong đoạn mã sau không nên được sử
dụng như một biến Class vì chỉ một danh sách duy nhất sẽ được chia sẻ bởi tất cả
các cá thể Dog:
class Dog:


tricks = []
variable

# mistaken use of a class

def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
# unexpectedly shared by
all dogs
['roll over', 'play dead']
Thiết kế chính xác của Class nên sử dụng mẫu thay đổi thay vì :

class Dog:
def __init__(self, name):
self.name = name
self.tricks = []
# creates a new empty list
for each dog
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
['roll over']
>>> e.tricks
['play dead']
1.4. Random Remarks


Thuộc tính dữ liệu ghi đè thuộc tính phương thức có cùng tên; để tránh xung đột
tên ngẫu nhiên, điều này có thể gây ra lỗi khó tìm trong các chương trình lớn, bạn
nên sử dụng một số loại quy ước để giảm thiểu cơ hội xung đột. Các quy ước có
thể bao gồm viết hoa tên phương thức, đặt tên cho các thuộc tính dữ liệu với một
chuỗi nhỏ duy nhất (có lẽ chỉ là dấu gạch dưới) hoặc sử dụng động từ cho các
phương pháp và danh từ cho các thuộc tính dữ liệu

Các thuộc tính dữ liệu có thể được tham chiếu bởi các phương thức cũng như bởi
người dùng thông thường (“clients”) của một Object. Nói cách khác, các Class
không thể sử dụng để triền khai các kiểu dữ liệu trừu tượng thuần túy.Trong thực
tế,khong có gì trong Python làm cho nó có thể thực thi ẩn dữ liệu- tất cả đều dựa

trên quy ước. (Mặt khác, việc thực hiện Python, được viết bằng C, hoàn toàn có
thể ẩn các chi tiết thực hiện và kiểm soát quyền truy cập vào một Object nếu cần
thiết;điều này có thể được sử dụng bởi các phần mở rộng cho
Python được viết bằng c).

Khách hàng nên sử dụng các thuộc tính dữ liệu với sự quan tâm - khách hàng có
thể làm rối loạn các bất biến được duy trì bởi các phương thức bằng cách dán lên
các thuộc tính dữ liệu của chúng. . Lưu ý rằng khách hàng có thể thêm thuộc tính
dữ liệu của riêng mình vào Object thể hiện mà không ảnh hưởng đến tính hợp lệ
của các phương thức, miễn là xung đột tên được tránh – một lần nữa,quy ước đặt
tên có thể tiết kiệm rất nhiều đau đầu ở đây.

Không có cách viết tắt để tham chiếu thuộc tính dữ liệu (hoặc các phương thức
khác!) Từ bên trong các phương thức. Tôi thấy rằng điều này thực sự làm tăng khả
năng đọc của các phương thức:không có khả năng gây nhầm lẫn giữa các biến cục
bộ và biến mẫu khi lướt qua một phương thức.
Thông thường, đối số đầu tiên của một phương thức được gọi là tự. Đây không là
gì hơn là một quy ước: tên tự hoàn toàn không có ý nghĩa đặc biệt đối với Python.
Tuy nhiên, lưu ý rằng bằng cách không tuân thủ quy ước, mã của bạn có thể ít đọc
được đối với các lập trình viên Python khác, và cũng có thể hiểu rằng chương
trình trình duyệt Class có thể được viết dựa trên quy ước đó


Bất kỳ Object hàm nào là một thuộc tính Class đều định nghĩa một phương thức
cho các cá thể của Class đó. Không cần thiết rằng định nghĩa hàm được bao bọc
bằng văn bản trong định nghĩa Class:gán một Object hàm cho một biến cục bộ
trong Class cũng là ok.ví dụ:
# Function defined outside the class
def f1(self, x, y):
return min(x, x+y)

class C:
f = f1
def g(self):
return 'hello world'
h = g
Bây giờ f, g và h là tất cả các thuộc tính của Class C tham chiếu đến các Object
hàm, và do đó chúng là tất cả các phương thức của các cá thể của C - h tương
đương chính xác với g. Lưu ý rằng thực hành này thường chỉ phục vụ để gây nhầm
lẫn cho người đọc của một chương trình.
Methods may call other methods by using method attributes of the self argument:
Các phương thức có thể gọi các phương thức khác bằng cách sử dụng các thuộc
tính phương thức của đối số tự:
class Bag:
def __init__(self):
self.data = []
def add(self, x):
self.data.append(x)
def addtwice(self, x):
self.add(x)
self.add(x)
Các phương thức có thể tham chiếu đến tên toàn cầu theo cùng cách với các hàm
bình thường. Phạm vi toàn cầu liên kết với một phương thức là mô-đun chứa định
nghĩa của nó. (Một Class không bao giờ được sử dụng như một phạm vi toàn cầu.)


Trong khi một người hiếm khi gặp một lý do chính đáng để sử dụng dữ liệu toàn
cục trong một phương thức, có nhiều cách sử dụng hợp pháp phạm vi toàn cục:
một điều, hàm và mô-đun được nhập vào phạm vi toàn cục có thể được sử dụng
bởi các phương thức, cũng như các hàm và Class được định nghĩa trong đó..
Thông thường, Class chứa phương thức được xác định chính nó trong phạm vi

toàn cục này và trong phần tiếp theo, chúng ta sẽ tìm thấy một số lý do chính đáng
tại sao một phương thức muốn tham chiếu Class riêng của nó
Mỗi giá trị là một Object, và do đó có một Class (còn được gọi là kiểu của nó). Nó
được lưu trữ như là Object .__ class__.
1.5. Inheritance
Mỗi giá trị là một Object, và do đó có một Class (còn được gọi là kiểu của nó). Nó
được lưu trữ như là Object .__ class__.
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>
Tên BaseClassName phải được định nghĩa trong một phạm vi chứa định nghĩa
Class dẫn xuất. Thay cho tên Class cơ sở, các biểu thức tùy ý khác cũng được cho
phép. Điều này có thể hữu ích, ví dụ, khi Class cơ sở được định nghĩa trong một
mô-đun khác:
class DerivedClassName(modname.BaseClassName):
Việc thực hiện định nghĩa Class dẫn xuất cũng giống như đối với một Class cơ sở.
Khi Object Class được xây dựng, Class cơ sở được ghi nhớ. Điều này được sử
dụng để giải quyết tham chiếu thuộc tính : nếu không tìm thấy thuộc tính được yêu
cầu trong Class, tìm kiếm sẽ tiến hành tìm trong Class cơ sở. Quy tắc này được áp
dụng đệ quy nếu chính Class cơ sở được bắt nguồn từ một số Class khác
Không có gì đặc biệt về việc khởi tạo các Class dẫn xuất: DerivedClassName ()
tạo ra một cá thể mới của Class đó. Các tham chiếu phương thức được giải quyết
như sau: thuộc tính Class tương ứng được tìm kiếm. , giảm dần chuỗi các Class cơ
sở nếu cần, và tham chiếu phương thức là hợp lệ nếu điều này tạo ra một Object
hàm.



Các Class có nguồn gốc có thể ghi đè lên các phương thức của các Class cơ sở
của chúng. Vì các phương thức không có đặc quyền đặc biệt khi gọi các phương
thức khác của cùng một Object, một phương thức của một Class cơ sở gọi một
phương thức khác được định nghĩa trong cùng một Class cơ sở có thể kết thúc gọi
một phương thức của một Class dẫn xuất ghi đè nó. (Đối với c lập trình viên C :
tất cả các phương thức trong Python đều có hiệu quả ảo.)
Một phương thức ghi đè trong một Class dẫn xuất có thể thực sự muốn mở rộng
thay vì chỉ đơn giản là thay thế phương thức Class cơ sở có cùng tên. Có một cách
đơn giản để gọi trực tiếp phương thức Class cơ sở: chỉ cần gọi
BaseClassName.methodname (tự, đối số). Điều này đôi khi hữu ích cho khách
hàng là tốt. (Lưu ý rằng điều này chỉ hoạt động nếu Class cơ sở có thể truy cập
dưới dạng BaseClassName trong phạm vi toàn cục .)
Python có hai chức năng nội tại làm việc với di sản :

 Sử dụng isinstance () để kiểm tra kiểu của một thể hiện: isinstance (obj, int)
sẽ là True chỉ khi obj .__ class__ là int hoặc một số Class bắt nguồn từ int.
 Dùng issubclass () để kiểm tra thừa kế Class: issubclass (bool, int) là True
vì bool là một Class con của int. Tuy nhiên, issubclass (float, int) là False vì
float không phải là một Class con của int..
1.5.1. Multiple Inheritance
Python cũng hỗ trợ một dạng đa thừa kế. Một định nghĩa Class với nhiều Class cơ
sở trông giống như sau:
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
Đối với hầu hết các trường hợp, trong các trường hợp đơn giản nhất, bạn có thể
nghĩ tìm kiếm các thuộc tính được kế thừa từ một Class cha như độ sâu đầu tiên,

từ trái sang phải, không tìm kiếm hai lần trong cùng một Class có chồng chéo
trong cấu trúc phân cấp. Vì vậy, nếu một thuộc tính không được tìm thấy trong
DerivedClassName, nó được tìm kiếm trong Base1, sau đó (đệ quy) trong các


Class cơ sở của Base1, và nếu nó không được tìm thấy ở đó, nó đã đã được tìm
kiếm trong Base2, vv.
Trong thực tế, nó hơi phức tạp hơn thế; thứ tự độ phân giải của phương thức thay
đổi động để hỗ trợ các cuộc gọi hợp tác đến super().Cách tiếp cận này được biết
đến trong một số ngôn ngữ đa thừa kế khác như gọi phương thức tiếp theo và
mạnh hơn so với siêu cuộc gọi được tìm thấy trong các ngôn ngữ kế thừa đơn.
Thứ tự động là cần thiết bởi vì tất cả các trường hợp thừa kế nhiều thể hiện một
hoặc nhiều mối quan hệ kim cương (nơi ít nhất một trong các Class cha có thể
được truy cập thông qua nhiều đường dẫn từ Class dưới cùng). Ví dụ,
tất cả các Class kế thừa từ Object, vì vậy bất kỳ trường hợp thừa
kế nào cũng cung cấp nhiều hơn một đường dẫn để tiếp cận
Object. Để giữ cho các Class cơ sở không bị truy cập nhiều hơn
một lần, thuật toán động tuyến tính thứ tự tìm kiếm theo cách
giữ nguyên thứ tự từ trái sang phải được chỉ định trong mỗi Class,
chỉ gọi mỗi cha mẹ một lần và đó là đơn điệu (nghĩa là một Class
có thể được phân Class mà không ảnh hưởng đến thứ tự ưu tiên
của cha mẹ của nó). Kết hợp với nhau, các thuộc tính này làm cho
nó có thể thiết kế các Class đáng tin cậy và mở rộng với nhiều
thừa kế. Để biết thêm chi tiết, hãy xem
/>1.6. Private Variables
Các biến cá thể "riêng tư" không thể truy cập trừ khi bên trong một Object không
tồn tại trong Python. Tuy nhiên, có một quy ước được theo sau bởi hầu hết mã
Python: một tên được đặt trước bằng dấu gạch dưới. (ví dụ: _spam) phải được coi
là một phần không công khai của API (cho dù đó là một hàm, một phương thức
hay một thành viên dữ liệu). Nó phải được coi là một chi tiết thực hiện và có thể

thay đổi mà không cần thông báo
Vì có một trường hợp sử dụng hợp lệ cho các thành viên private-class (cụ thể là để
tránh xung đột tên của các tên với các tên được xác định bởi các Class con), có sự
hỗ trợ hạn chế cho một cơ chế như vậy, được gọi là mangling name. Bất kỳ số
nhận dạng nào của biểu mẫu __spam (ít nhất hai dấu gạch dưới hàng đầu, tối đa
một dấu gạch dưới sau) được thay thế bằng văn bản bằng _classname__spam,
trong đó classname là tên Class hiện tại với các dấu gạch dưới hàng đầu bị tước.
Việc xén này được thực hiện mà không quan tâm đến vị trí cú pháp của mã định
danh, miễn là nó xảy ra trong định nghĩa của một Class.


.
Tên mangling là hữu ích cho phép các Class con ghi đè lên các phương thức mà
không phá vỡ các lời gọi phương thức intraclass. Ví dụ:
class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.items_list.append(item)
__update = update
update() method

# private copy of original

class MappingSubclass(Mapping):
def update(self, keys, values):
# provides new signature for update()
# but does not break __init__()

for item in zip(keys, values):
self.items_list.append(item)
Lưu ý rằng các quy tắc mangling được thiết kế chủ yếu để tránh tai nạn; nó vẫn có
thể truy cập hoặc sửa đổi một biến được coi là riêng tư. Điều này thậm chí có thể
hữu ích trong các trường hợp đặc biệt ,chẳng hạn như trong trình gỡ
lỗi
Lưu ý rằng mã được chuyển tới exec () hoặc eval () không xem xét tên Class của
Class gọi là Class hiện tại; điều này tương tự như ảnh hưởng của câu lệnh chung,
hiệu ứng của nó cũng bị hạn chế đối với mã được biên dịch byte cùng
nhau. Các hạn chế tương tự áp dụng cho getattr (), setattr () và
delattr (), cũng như khi tham chiếu __dict__ trực tiếp.
1.7. Odds and Ends
Đôi khi nó rất hữu ích để có một kiểu dữ liệu tương tự như "bản ghi" Pascal hoặc
C "struct", bó lại với nhau một vài mục dữ liệu có tên. Một định nghĩa Class trống
sẽ làm độc đáo:


class Employee:
pass
john = Employee()

# Create an empty employee record

# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
Một đoạn mã Python dự kiến một loại dữ liệu trừu tượng cụ thể thường có thể
được chuyển qua một Class mô phỏng các phương thức của kiểu dữ liệu đó thay
thế. Ví dụ, nếu bạn có một hàm định dạng một số dữ liệu từ một Object tập tin. bạn

có thể định nghĩa một Class với các phương thức read () và readline () để lấy dữ
liệu từ một bộ đệm chuỗi thay vào đó, và chuyển nó thành một đối số.
Các Object phương thức instance cũng có các thuộc tính: m .__ self__ là Object
instance với phương thức m () và m .__ func__ là Object hàm tương ứng với
phương thức.
1.8. Iterators
:
Bây giờ bạn có thể nhận thấy rằng hầu hết các Object container có thể được lặp
lại bằng cách sử dụng câu lệnh for
for element in [1, 2, 3]:
print(element)
for element in (1, 2, 3):
print(element)
for key in {'one':1, 'two':2}:
print(key)
for char in "123":
print(char)
for line in open("myfile.txt"):
print(line, end='')
Kiểu truy cập này rõ ràng, ngắn gọn và thuận tiện. Việc sử dụng các trình vòng lặp
tràn lan và hợp nhất Python. Phía sau hậu trường, câu lệnh for gọi iter () trên
Object container. Hàm trả về một Object iterator định nghĩa phương thức __next


__ () để truy cập các phần tử trong vùng chứa một lần. Khi không còn yếu tố nào
nữa , __next __ () đặt ra một ngoại lệ StopIteration để báo cho vòng lặp for chấm
dứt. Bạn có thể gọi phương thức __next __ () bằng hàm dựng sẵn tiếp theo (); ví
dụ này cho thấy nó hoạt động như thế nào:
>>>
>>> s = 'abc'

>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
next(it)
StopIteration
Sau khi nhìn thấy các cơ chế đằng sau giao thức lặp, nó rất dễ dàng để thêm hành
vi của trình lặp vào các Class của bạn. Xác định phương thức __iter __ () trả về
một Object với phương thức trả về một Object với phương thức __next __ (). Nếu
Class định nghĩa __next __ (), thì __iter __ () chỉ có thể trả về tự:
class Reverse:
"""Iterator for looping over a sequence
backwards."""
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1



return self.data[self.index]

>>>
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
...
print(char)
...
m
a
p
s
1.1. Generators
Generators là một công cụ đơn giản và mạnh mẽ để tạo trình lặp. Chúng được viết
giống như các hàm thông thường nhưng sử dụng câu lệnh yield bất cứ khi nào
chúng muốn trả về dữ liệu. Mỗi lần next () được gọi trên nó, máy phát lại sẽ tiếp
tục lại
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
>>>
>>> for char in reverse('golf'):
...
print(char)
...
f

l
o
g
Bất cứ điều gì có thể được thực hiện với các máy phát cũng có thể được thực hiện
với các trình vòng lặp dựa trên Class như được mô tả trong phần trước. Điều làm
cho các máy phát nhỏ gọn đến mức các phương thức __iter __ () và __next __ ()
được tạo tự động.
Một tính năng quan trọng khác là các biến cục bộ và trạng thái thực thi được lưu
tự động giữa các cuộc gọi. Điều này làm cho hàm dễ viết và rõ ràng hơn nhiều so
với cách tiếp cận sử dụng các biến mẫu như self.index và self.data


Ngoài việc tạo phương thức tự động và lưu trạng thái chương trình, khi các máy
phát điện chấm dứt, chúng sẽ tự động tăng StopIteration. Kết hợp, các tính năng
này giúp dễ dàng tạo các trình vòng lặp mà không cần nỗ lực nhiều hơn là viết một
hàm thông thường.
1.10. Generator Expressions
Một số trình tạo đơn giản có thể được mã hóa gọn gàng như các biểu thức bằng
cách sử dụng một cú pháp tương tự như việc hiểu danh sách nhưng với các dấu
ngoặc đơn thay vì các dấu ngoặc vuông. Các biểu thức này được thiết kế cho các
tình huống mà máy phát điện được sử dụng ngay lập tức bởi một hàm kèm theo.
Biểu thức máy phát điện nhỏ gọn hơn nhưng ít linh hoạt hơn so với định nghĩa
máy phát điện đầy đủ và có xu hướng thân thiện với bộ nhớ hơn so với việc hiểu
danh sách tương đương.
Ví dụ
>>>
>>> sum(i*i for i in range(10))
of squares
285
>>> xvec = [10, 20, 30]

>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec))
product
260

# sum

# dot

>>> from math import pi, sin
>>> sine_table = {x: sin(x*pi/180) for x in range(0,
11)}
>>> unique_words = set(word
in line.split())

for line in page

for word

>>> valedictorian = max((student.gpa, student.name) for
student in graduates)
>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1, -1, -1))
['f', 'l', 'o', 'g']






×