Lessons VII : Lớp trong Python

IV. Lp trong Python:

1. Cú pháp đnh nghĩa lp:

 

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 lp:

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 thuc 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. Thc th lp:

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 thc 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 thc:

 

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ế tha đơ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ế tha

        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. Các k thut v chng hàm, chng toán t, hàm và d liu thành phn tĩnh:

6.1. Chng 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 Chng 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 liu thành phn tĩnh

a. Hàm (hay phương thc 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 @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à selfvào.

 

        b. D liu thành phn 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 thut Odds and Ends đi vi lp

          Trong Python thông đôi khi k thut này cũng được s dng đ to ra các kiu d liu ging record trong Pascal hoc struct trong C, đng thi cùng vi mt vài thành phn d liu được đt tên. Ta dùng mt đnh nghĩa lp rng đ thc hin hin điu 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 thc đc bit:

 

        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 thc khi to:

__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 thc 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 thuc 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ụng phầ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

 

  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: