IV. Lớp trong Python:
1. Cú pháp định nghĩa lớp:
Python sử dụng một cách thức khá đơn giản để định nghĩa một lớp.Cú
pháp như sau:
class ClassName:
<statement-1>
.
.
.
<statement-N>
Cũng
giống như định nghĩa hàm, một lớp phải được định nghĩa trước khi nó có thể sử dụng.Thực
tế, các lệnh viết trong định nghĩa lớp thường sửdụng là các định nghĩa hàm.
Đối
tượng lớp sẽ được tạo ra ngay sau khi định nghĩa lớp kết thúc.
2. Các đối tượng lớp:
Các đối tượng lớp trong Python hỗ trợ 2 thao tác : tham
chiếu thuộc tính và thực thể.
2.1. Tham chiếu thuộc tính
Cú pháp tham chiếu thuộc tính của các đối tượng lớp cũng như cú pháp
chuẩn cho tất cả các tham chiếu thuộc tính trong Python:
Obj.name
Giá
trị của tên thuộc tính là tất cả các tên mà nằm trong không gian tên
của lớp khi đối tượng được tạo ra.
Xét một lớp được định
nghĩa như sau:
class MyClass:
"A simple example
class"
i = 12345
def f(self):
return
'hello world'
Thì MyClass.i và
MyClass.f là các giá trị tham chiếu thuộc tính, trả về một số nguyên
và một đối tượng hàm.
2.2. Thực thể lớp:
Thực
thể lớp sử dụng ký hiệu hàm, mô tả đối tượng lớp như là một
hàm không tham số và trả về một thực thể mới của lớp đó. Ví
dụ (giả sử đã có lớp MyClass ở trên):
x = MyClass()
tạo ra một thực thể của
lớp này và đăng kí đối tượng tạo ra với biến cục bộ x.
Việc
thao tác một thực thể ( gọi một đối tượng lớp) tạo ra một đối tượng
rỗng. Có nhiều lớp mục đích là để tạo ra các đối tượng với các thực thể
thỏa mãn một trạng thái khởi tạo xác định. Vì thế một lớp có thể định
nghĩa một phương thức đặc biệt tên là __init__( ), giống như sau:
def __init__(self):
self.data = []
Khi một lớp định nghĩa
một phương thức __init__( ), thực thể lớp tự động gọi đến hàm __init__( )
cho thực thể vừa được tạo ra. Vì thế trong ví dụ trên, một thực thể khởi tạo
mới có thể thu được bởi:
x = MyClass()
Tất nhiên, hàm __init__( )
có thể có đối số để linh hoạt hơn. Trong trường hợp đó, các đối số được truyền
đến toán tử thực thể lớp thông qua hàm __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)
3. Các đối tượng thực thể:
Đối
tượng thực thể chỉ cho phép thao tác duy nhất là tham chiếu thuộc
tính. Có 2 loại thuộc tính là: thuộc tính dữ liệu và phương thức.
Thuộc
tính dữ liệu không cần phải khai báo, giống như biến cục bộ, chúng tự sinh
ra ngay lần đầu gọi đến chúng. Ví dụ, nếu x là một thực thể của
MyClass tạo ra ở trên, thì đoạn mã dưới đây sẽ in ra giá trị 16
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 thứ 2 là phương thức. Một phương thức là một
hàm thuộc một đối tượng. Trong ví dụ trên MyClass.f là một hàm, còn
x.f là một đối tượng phương thức, không phải một đối tượng hàm.
4. Các đối tượng phương thức:
Thông thường một đối tượng phương thức được gọi sau khi nó được chấp nhận:
x.f
( )
Trong
ví dụ MyClass, lời gọi trên sẽ trả về xâu ‘hello world’. Tuy
nhiên, không cần thiết phải gọi 1 phương thức theo đúng cách như trên,
vì x.f làmột đối tượng phương thức, có thể được lưu trữ lại và gọi ở lần
tiếp theo, ví dụ:
xf = x.f
while True:
print xf( )
Đoạn mã này sẽ tiếp tục in ra dòng chữ ‘hello world’ cho đến khi ngừng chương
trình.
5. Tính kế thừa:
5.1. Kế thừa đơn
Trong Python không có khái niệm Protected như C++ , chỉ có các hàm ,dữ liệu
thành phần public hay private , và chúng được
phân biệt qua tên , những tên bắt đầu bằng __ và kết thúc có tối đa 1 dấu _ là private
Ví dụ __x , __x_ , __abc là những biến
private , __y__ ,a ,bc , xyz__ , x_ là những biến public . Tương tự như vậy
với tên hàm.
Các biến và phương thức private chỉ có thể truy cập
nội bộ trong class đó , các biến và phương thức public có
thể truy xuất trong toàn bộ chương trình. Chỉ có 1 loại kế thừa
là public.
Cú pháp khai báo một lớp con kế thừa như sau
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>
Xét ví dụ sau:
>>> class A:
sa='Day
la thuoc tinh cua lop A'
__pri='Day la thuoc tinh rieng cua A'
def __init__(self):
print 'Ham
khoi tao cua lop A'
pass
>>> class B(A):
sb='Day
la thuoc tinh cua lop B'
def __init__(self):
print 'Ham
khoi tao cua lop B'
>>> b=B()
Ham khoi tao cua lop B
>>> b.sa
'Day la thuoc tinh cua
lop A'
>>> b.sb
'Day la thuoc tinh cua
lop B'
>>> b.__pri
Traceback (most recent
call last):
File
"<pyshell#261>", line 1, in <module>
b.__pri
AttributeError: B
instance has no attribute '__pri'
>>>
Như thấy ở trên
lớp A có 1 thuộc tính public là sa và 1 thuộc tính private là __pri mà lớp B kế thừa
từ A cũng không truy xuất được. Một điểm lưu ý nữa là hàm khởi tạo của B
không tự gọi hàm khởi tạo của A , do đó người lập trình phải gọi
hàm khởi tạo của A .
5.2.Đa kế thừa
Khai báo Đa kế thừa cũng như đơn kế thừa
,chỉ việc thêm danh sách các lớp cơ sở vào trong cặp dấu () sau
tên lớp:
class DerivedClassName(Base1,
Base2, Base3):
<statement-1>
.
.
.
<statement-N>
Do Python không tự gọi hàm khởi tạo của các
lớp cha nên thứ tự kế thừa không quan trọng.
Ví dụ đa kế thừa:
>>> class A:
sa='Day
la thuoc tinh lop A'
def __init__(self):
print 'Ham
khoi tao lop A'
pass
>>> class B:
sb='Day
la thuoc tinh lop B'
def __init__(self):
print 'Ham
khoi tao lop B'
pass
>>> class C(A,B):
sc='Day
la thuoc tinh lop C'
def __init__(self):
print 'Ham
khoi tao lop C'
pass
>>> c=C()
Ham khoi tao lop C
>>> c.sa
'Day la thuoc tinh lop
A'
>>> c.sb
'Day la thuoc tinh lop
B'
>>> c.sc
'Day la thuoc tinh lop
C'
>>>
6.1. Chồng hàm trong class
Python không hỗ trợ chồng hàm trong class khi mà không có một khai
báo chặt chẽ về kiểu của dữ liệu các tham số truyền cho hàm
, kiểu của chúng được xác định tuỳ theo giá trị truyền cho hàm lúc gọi
hàm.
6.2 Chồng toán tử
Chồng toán tử trong Python được thực hiện bằng
việc khai báo đè lên một số tên đặc biệt.Có rất nhiều toán tử có thể chồng
trong Python.
Ví dụ phương
thức __add__(self,x) sẽ định nghĩa lại phép cộng
__sub__(self,x) sẽ định nghĩa lại phép trừ
selfở đây là tham số bắt buộc phải có đối
với mọi phương thức , nó giống con trỏthis trong C++
Xét lớp sau
>>> class A:
x=1
y=2.0
def __add__(self,a):
self.x-=a
self.y-=a
>>> a
<__main__.A instance at 0x00BBCF58>
>>> a=A()
>>> a.x
1
>>> a.y
2.0
>>> a+1
>>> a.x
0
>>> a.y
1.0
>>>
6.3 Hàm và dữ liệu thành phần
tĩnh
a. Hàm (hay phương
thức tĩnh ) được khai báo trong Python như sau
class C:
@staticmethod
def f(arg1,
arg2, ...): ...
Hoặc
class C:
@classmethod
def f(arg1,arg2,…):….
Sự khác nhau giữa @staticmethod và @classmethod như sau:
- Nếu một hàm khai báo là @classmethod
thì khi gọi hàm, hệ thống sẽ ngầm định truyền cho hàm tham số đầu
tiên là thực thể của chính class đó , do đó hàm phải luôn sử dụng
arg1 như là self (giống con trỏthis trong C++
).
- Nếu một hàm khai báo là @staticmethod
thì khi gọi hàm, hệ thống sẽ không ngầm định truyền cho hàm tham số đầu
tiên là thực thể của chính class đó , do đó hàm này không
thể biết được thực thể nào đã gọi mình.
Ví dụ:
>>> class C:
@staticmethod
def hello():
print 'Hello'
>>> C.hello()
Hello
class D:
@classmethod
def hello():
print 'Hello'
>>> D.hello()
Traceback (most recent call last):
File "<pyshell#73>", line
1, in <module>
D.hello()
TypeError: hello() takes no arguments (1
given)
>>>
Lỗi xảy ra trong lớp D là do hàm hello khi khai
báo không nhận 1 tham số nào, nhưng khi được gọi, hệ thống tự thêm
tham số đàu tiên là self vào.
b. Dữ liệu thành phần tĩnh
Dữ liệu thành phần
tĩnh của lớp được khai báo ngay sau tên lớp
>>> class A:
counter=0
def __init__(self):
self.__class__.counter+=1
print 'Thuoc
tinh tinh',A.counter
print 'Thuoc
tinh cua lop',self.counter
>>> A.counter
0
>>> a=A()
Thuoc tinh tinh 1
Thuoc tinh cua lop 1
>>> b=A()
Thuoc tinh tinh 2
Thuoc tinh cua lop 2
>>>
Thuộc tính tĩnh được
chia sẻ bởi tất cả các thực thể của lớp và bản thân tên lớp đó
7. Kỹ thuật Odds and Ends
đối với lớp
Trong Python thông đôi khi kỹ thuật này cũng được sử dụng để tạo
ra các kiểu dữ liệu giống record trong Pascal hoặc struct trong C, đồng thời
cùng với một vài thành phần dữ liệu được đặt tên. Ta dùng một định
nghĩa lớp rỗng để thực hiện hiện điều này:
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
8. Các phương thức đặc biệt:
Có một số các phương thức được dành để sử dụng
với các mục đích đặc biệt, nó được kết thúc với 2 dấu gạch dưới. Thông thường
các phương thức đó được bắt đầu với một dấu gạch dưới để đánh dấu nó là
‘private’ với phạm vi mà nó được sử dụng trong đó.
Phương thức khởi tạo:
__init__
Một trong những mục đích của nó là để xây
dựng một thực thể lớp, và tên đặc biệt cho phương thức này là ‘__init__’
Hàm __init__() được gọi trược khi một thực thể được trả về. Ví dụ:
class A:
def __init__(self):
print 'A.__init__()'
a = A()
sẽ đưa ra
A.__init__()
__init__() có thể có
đối số, trong trường hợp cần thông qua các đối số để tạo ra một thực
thể. Ví dụ:
class Foo:
def __init__
(self, printme):
print printme
foo = Foo('Hi!')
sẽ đưa ra:
Hi!
Sau đây là một ví dụ chỉ ra
sự khác nhau giữa sử dụng và không sử dụng __init__():
class Foo:
def __init__
(self, x):
print x
foo = Foo('Hi!')
class Foo2:
def setx(self,
x):
print x
f = Foo2()
Foo2.setx(f,'Hi!')
Sẽ in ra
Hi!
Hi!
Phương thức miêu tả:
__str__
Biến
đổi một đối tượng thành một xâu, như là với câu lệnh print hoặc với
hàm str(), nó có thể được đưa ra bởi __str__. Thông thường __str__ trảvề một
bản đã định dạng của nội dung đối tượng, điều này sẽ không được thường
xuyên thực hiện. Ví dụ:
class Bar:
def __init__
(self, iamthis):
self.iamthis = iamthis
def __str__
(self):
return self.iamthis
bar = Bar('apple')
print bar
sẽ in ra:
apple
__repr__
Hàm này cũng giống như __str__(). __repr__
được sử dụng để trả về biểu diển của một đối tượng trong dạng
xâu. Thông thường nó có thể trả vềđối tượng gốc. Ví dụ:
class Bar:
def __init__
(self, iamthis):
self.iamthis = iamthis
def __repr__(self):
return "Bar('%s')"
% self.iamthis
bar = Bar('apple')
print bar
sẽ in ra:
Bar('apple')
Các thuộc tính:
__setattr__
Đây là hàm quản lí các
thuộc tính trong một lớp. Nó được cung cấp với tên và giá trị các biến
được đăng kí. Tất nhiên, mỗi lớp có một __setattr__ mặc định đơn giản để thiết
lập giá trị của các biến, nhưng cũng có thể gạt bỏ nó:
>>> class Unchangable:
... def __setattr__(self,
name, value):
... print "Nice
try"
...
>>> u = Unchangable()
>>> u.x = 9
Nice try
>>> u.x
Traceback (most recent
call last):
File
"<stdin>", line 1, in ?
AttributeError:
Unchangable instance has no attribute 'x'
__getattr___
Cũng giống với
__setattr__, trừ hàm này được gọi khi ta truy nhập vào một
thành viên lớp, và mặc định đơn giản trả về giá trị.
>>> class
HiddenMembers:
... def __getattr__(self,
name):
... return "You
don't get to see " + name
...
>>> h = HiddenMembers()
>>> h.anything
"You don't get to
see anything"
__delattr__
Hàm này được gọi để xóa
một đối tượng
>>> class Permanent:
... def __delattr__(self,
name):
... print name,
"cannot be deleted"
...
>>> p = Permanent()
>>> p.x = 9
>>> del p.x
x cannot be deleted
>>> p.x
9
9. Đóng gói lớp:
Bởi
vì tất cả các thành phần của một lớp trong Python là có thể truy nhập
bởi các hàm và phương thức bên ngoài lớp đó, không có cách nào để đóng gói
các hàm ghi đè __getattr__, __setattr__ và __delattr__. Tuy nhiên, thực tế,
để tạo ta một lớp hoặc một module để đơn giản người ta chỉ sử dụngphần giao
diện đã dành cho và tránh truy cập đến vùng làm việc của module khác
Hãy like nếu bài viết có ích →
Kết bạn với gisgpsrs trên Facebook
để nhận bài viết mới nóng hổi
Không có nhận xét nào:
Đăng nhận xét