Python hướng đối tượng - Tính năng nâng cao
Trong phần này, chúng ta sẽ xem xét một số tính năng nâng cao mà Python cung cấp
Cú pháp cốt lõi trong thiết kế Lớp của chúng tôi
Trong phần này, chúng ta sẽ xem xét cách Python cho phép chúng ta tận dụng các toán tử trong các lớp của chúng ta. Python phần lớn là các đối tượng và phương thức gọi các đối tượng và điều này thậm chí vẫn tiếp diễn ngay cả khi nó bị ẩn bởi một số cú pháp thuận tiện.
>>> var1 = 'Hello'
>>> var2 = ' World!'
>>> var1 + var2
'Hello World!'
>>>
>>> var1.__add__(var2)
'Hello World!'
>>> num1 = 45
>>> num2 = 60
>>> num1.__add__(num2)
105
>>> var3 = ['a', 'b']
>>> var4 = ['hello', ' John']
>>> var3.__add__(var4)
['a', 'b', 'hello', ' John']
Vì vậy, nếu chúng ta phải thêm magic method __add__ vào các lớp của riêng mình, chúng ta cũng có thể làm điều đó. Hãy cố gắng làm điều đó.
Chúng ta có một lớp gọi là Sumlist có một cấu trúc __init__ nhận danh sách làm đối số được gọi là my_list.
class SumList(object):
def __init__(self, my_list):
self.mylist = my_list
def __add__(self, other):
new_list = [ x + y for x, y in zip(self.mylist, other.mylist)]
return SumList(new_list)
def __repr__(self):
return str(self.mylist)
aa = SumList([3,6, 9, 12, 15])
bb = SumList([100, 200, 300, 400, 500])
cc = aa + bb # aa.__add__(bb)
print(cc) # should gives us a list ([103, 206, 309, 412, 515])
Đầu ra
[103, 206, 309, 412, 515]
Nhưng có nhiều phương pháp được quản lý nội bộ bởi những phương pháp ma thuật khác. Dưới đây là một số trong số chúng,
'abc' in var # var.__contains__('abc')
var == 'abc' # var.__eq__('abc')
var[1] # var.__getitem__(1)
var[1:3] # var.__getslice__(1, 3)
len(var) # var.__len__()
print(var) # var.__repr__()
Kế thừa từ các loại cài sẵn
Các lớp cũng có thể kế thừa từ các loại tích hợp, điều này có nghĩa là kế thừa từ bất kỳ loại tích hợp nào và tận dụng tất cả các chức năng được tìm thấy ở đó.
Trong ví dụ dưới đây, chúng tôi đang kế thừa từ từ điển nhưng sau đó chúng tôi đang triển khai một trong các phương thức __setitem__ của nó. Điều này (setitem) được gọi khi chúng ta đặt khóa và giá trị trong từ điển. Vì đây là một phương pháp ma thuật, điều này sẽ được gọi ngầm.
class MyDict(dict):
def __setitem__(self, key, val):
print('setting a key and value!')
dict.__setitem__(self, key, val)
dd = MyDict()
dd['a'] = 10
dd['b'] = 20
for key in dd.keys():
print('{0} = {1}'.format(key, dd[key]))
Đầu ra
setting a key and value!
setting a key and value!
a = 10
b = 20
Hãy mở rộng ví dụ trước của chúng tôi, dưới đây chúng tôi đã gọi hai phương thức ma thuật có tên __getitem__ và __setitem__ tốt hơn được gọi khi chúng tôi xử lý chỉ mục danh sách.
# Mylist inherits from 'list' object but indexes from 1 instead for 0!
class Mylist(list): # inherits from list
def __getitem__(self, index):
if index == 0:
raise IndexError
if index > 0:
index = index - 1
return list.__getitem__(self, index) # this method is called when
# we access a value with subscript like x[1]
def __setitem__(self, index, value):
if index == 0:
raise IndexError
if index > 0:
index = index - 1
list.__setitem__(self, index, value)
x = Mylist(['a', 'b', 'c']) # __init__() inherited from builtin list
print(x) # __repr__() inherited from builtin list
x.append('HELLO'); # append() inherited from builtin list
print(x[1]) # 'a' (Mylist.__getitem__ cutomizes list superclass
# method. index is 1, but reflects 0!
print (x[4]) # 'HELLO' (index is 4 but reflects 3!
Đầu ra
['a', 'b', 'c']
a
HELLO
Trong ví dụ trên, chúng tôi đặt một danh sách ba mục trong Mylist và phương thức __init__ ngầm được gọi và khi chúng tôi in phần tử x, chúng tôi nhận được danh sách ba mục (['a', 'b', 'c']). Sau đó, chúng tôi thêm một phần tử khác vào danh sách này. Sau đó, chúng tôi yêu cầu chỉ mục 1 và chỉ mục 4. Nhưng nếu bạn thấy kết quả đầu ra, chúng tôi đang nhận phần tử từ (chỉ mục-1) những gì chúng tôi đã yêu cầu. Như chúng ta biết lập chỉ mục danh sách bắt đầu từ 0 nhưng ở đây việc lập chỉ mục bắt đầu từ 1 (đó là lý do tại sao chúng ta nhận được mục đầu tiên của danh sách).
Quy ước đặt tên
Trong phần này, chúng ta sẽ xem xét các tên mà chúng ta sẽ sử dụng cho các biến, đặc biệt là các biến riêng và các quy ước được sử dụng bởi các lập trình viên Python trên toàn thế giới. Mặc dù các biến được chỉ định là riêng tư nhưng không có quyền riêng tư trong Python và điều này theo thiết kế. Giống như bất kỳ ngôn ngữ được ghi chép đầy đủ nào khác, Python có các quy ước đặt tên và kiểu mà nó thúc đẩy mặc dù nó không thực thi chúng. Có một hướng dẫn phong cách được viết bởi “Guido van Rossum” the originator of Python, that describe the best practices and use of name and is called PEP8. Here is the link for this, https://www.python.org/dev/peps/pep-0008/
PEP là viết tắt của đề xuất nâng cao Python và là một loạt tài liệu được phân phối giữa cộng đồng Python để thảo luận về các thay đổi được đề xuất. Ví dụ, nó được khuyến nghị tất cả,
- Tên mô-đun - all_lower_case
- Tên lớp và tên ngoại lệ - CamelCase
- Tên toàn cầu và tên địa phương - all_lower_case
- Các hàm và tên phương thức - all_lower_case
- Hằng số - ALL_UPPER_CASE
Đây chỉ là đề xuất, bạn có thể thay đổi nếu bạn muốn. Nhưng vì hầu hết các nhà phát triển tuân theo các khuyến nghị này, vì vậy tôi có thể mã của bạn khó đọc hơn.
Tại sao phải tuân theo quy ước?
Chúng tôi có thể tuân theo khuyến nghị PEP mà nó cho phép chúng tôi nhận được,
- Quen thuộc hơn với đại đa số các nhà phát triển
- Rõ ràng hơn cho hầu hết người đọc mã của bạn.
- Sẽ phù hợp với phong cách của những người đóng góp khác làm việc trên cùng một cơ sở mã.
- Dấu ấn của một nhà phát triển phần mềm chuyên nghiệp
- Mọi người sẽ chấp nhận bạn.
Đặt tên biến - 'Công khai' và 'Riêng tư'
Trong Python, khi chúng ta xử lý các mô-đun và lớp, chúng ta chỉ định một số biến hoặc thuộc tính là private. Trong Python, không tồn tại biến cá thể “Riêng tư” mà không thể truy cập được ngoại trừ bên trong một đối tượng. Riêng tư đơn giản có nghĩa là chúng đơn giản không nhằm mục đích được sử dụng bởi những người sử dụng mã thay vào đó chúng được sử dụng trong nội bộ. Nói chung, một quy ước đang được hầu hết các nhà phát triển Python tuân theo, chẳng hạn như một tên có dấu gạch dưới. _attrval (ví dụ bên dưới) phải được coi là một phần không công khai của API hoặc bất kỳ mã Python nào, cho dù đó là hàm, phương thức hay thành viên dữ liệu. Dưới đây là quy ước đặt tên mà chúng tôi tuân theo,
Các thuộc tính hoặc biến công khai (dự định được sử dụng bởi nhà nhập khẩu của mô-đun này hoặc người dùng của lớp này) -regular_lower_case
Các thuộc tính hoặc biến riêng tư (sử dụng nội bộ bởi mô-đun hoặc lớp) -_single_leading_underscore
Các thuộc tính riêng tư không nên được phân lớp -__double_leading_underscore
Thuộc tính ma thuật -__double_underscores__(sử dụng chúng, không tạo chúng)
class GetSet(object):
instance_count = 0 # public
__mangled_name = 'no privacy!' # special variable
def __init__(self, value):
self._attrval = value # _attrval is for internal use only
GetSet.instance_count += 1
@property
def var(self):
print('Getting the "var" attribute')
return self._attrval
@var.setter
def var(self, value):
print('setting the "var" attribute')
self._attrval = value
@var.deleter
def var(self):
print('deleting the "var" attribute')
self._attrval = None
cc = GetSet(5)
cc.var = 10 # public name
print(cc._attrval)
print(cc._GetSet__mangled_name)
Đầu ra
setting the "var" attribute
10
no privacy!