Python hướng đối tượng - Khối xây dựng
Trong chương này, chúng ta sẽ thảo luận chi tiết về các thuật ngữ hướng đối tượng và các khái niệm lập trình. Class chỉ là một nhà máy cho một ví dụ. Nhà máy này chứa bản thiết kế mô tả cách tạo các phiên bản. Một cá thể hoặc đối tượng được xây dựng từ lớp. Trong hầu hết các trường hợp, chúng ta có thể có nhiều hơn một trường hợp của một lớp. Mọi cá thể đều có một tập thuộc tính và các thuộc tính này được định nghĩa trong một lớp, vì vậy mọi cá thể của một lớp cụ thể được mong đợi có các thuộc tính giống nhau.
Gói lớp: Hành vi và trạng thái
Một lớp sẽ cho phép bạn kết hợp các hành vi và trạng thái của một đối tượng. Quan sát sơ đồ sau để hiểu rõ hơn -
Những điểm sau đáng chú ý khi thảo luận về nhóm lớp:
Từ behavior giống hệt với function - nó là một đoạn mã thực hiện điều gì đó (hoặc thực hiện một hành vi)
Từ state giống hệt với variables - nó là nơi lưu trữ các giá trị trong một lớp.
Khi chúng ta khẳng định một hành vi và trạng thái của lớp cùng nhau, điều đó có nghĩa là một lớp đóng gói các hàm và biến.
Các lớp có các phương thức và thuộc tính
Trong Python, việc tạo một phương thức xác định một hành vi của lớp. Phương thức từ là tên OOP được đặt cho một hàm được định nghĩa trong một lớp. Tóm lại -
Class functions - là từ đồng nghĩa với methods
Class variables - là từ đồng nghĩa với name attributes.
Class - một bản thiết kế cho một ví dụ có hành vi chính xác.
Object - một trong những thể hiện của lớp, thực hiện chức năng được định nghĩa trong lớp.
Type - chỉ ra lớp mà cá thể thuộc về
Attribute - Bất kỳ giá trị đối tượng nào: object.attribute
Method - một "thuộc tính có thể gọi" được xác định trong lớp
Quan sát đoạn mã sau đây chẳng hạn:
var = “Hello, John”
print( type (var)) # ‘str’> or <class 'str'>
print(var.upper()) # upper() method is called, HELLO, JOHN
Sáng tạo và Thuyết minh
Đoạn mã sau đây cho thấy cách tạo lớp đầu tiên của chúng ta và sau đó là phiên bản của nó.
class MyClass(object):
pass
# Create first instance of MyClass
this_obj = MyClass()
print(this_obj)
# Another instance of MyClass
that_obj = MyClass()
print (that_obj)
Ở đây chúng tôi đã tạo một lớp có tên là MyClassvà không làm bất kỳ nhiệm vụ nào. Đối sốobject trong MyClass lớp liên quan đến kế thừa lớp và sẽ được thảo luận trong các chương sau. pass trong đoạn mã trên chỉ ra rằng khối này trống, đó là định nghĩa lớp trống.
Hãy để chúng tôi tạo một phiên bản this_obj của MyClass() lớp và in nó như được hiển thị -
<__main__.MyClass object at 0x03B08E10>
<__main__.MyClass object at 0x0369D390>
Ở đây, chúng tôi đã tạo một phiên bản của MyClass.Mã hex đề cập đến địa chỉ nơi đối tượng đang được lưu trữ. Một trường hợp khác đang trỏ đến một địa chỉ khác.
Bây giờ chúng ta hãy xác định một biến bên trong lớp MyClass() và lấy biến từ thể hiện của lớp đó như được hiển thị trong đoạn mã sau:
class MyClass(object):
var = 9
# Create first instance of MyClass
this_obj = MyClass()
print(this_obj.var)
# Another instance of MyClass
that_obj = MyClass()
print (that_obj.var)
Đầu ra
Bạn có thể quan sát kết quả sau khi thực thi đoạn mã được đưa ra ở trên:
9
9
Như thể hiện biết nó được khởi tạo từ lớp nào, vì vậy khi được yêu cầu một thuộc tính từ một thể hiện, thể hiện sẽ tìm thuộc tính và lớp. Đây được gọi làattribute lookup.
Phương pháp phiên bản
Một hàm được định nghĩa trong một lớp được gọi là method.Phương thức thể hiện yêu cầu một thể hiện để gọi nó và không yêu cầu trình trang trí. Khi tạo một phương thức phiên bản, tham số đầu tiên luôn làself. Mặc dù chúng ta có thể gọi nó (tự) bằng bất kỳ tên nào khác, nhưng chúng ta nên sử dụng self, vì nó là một quy ước đặt tên.
class MyClass(object):
var = 9
def firstM(self):
print("hello, World")
obj = MyClass()
print(obj.var)
obj.firstM()
Đầu ra
Bạn có thể quan sát kết quả sau khi thực thi đoạn mã được đưa ra ở trên:
9
hello, World
Lưu ý rằng trong chương trình trên, chúng ta đã định nghĩa một phương thức có đối số là self. Nhưng chúng ta không thể gọi phương thức vì chúng ta chưa khai báo bất kỳ đối số nào cho nó.
class MyClass(object):
def firstM(self):
print("hello, World")
print(self)
obj = MyClass()
obj.firstM()
print(obj)
Đầu ra
Bạn có thể quan sát kết quả sau khi thực thi đoạn mã được đưa ra ở trên:
hello, World
<__main__.MyClass object at 0x036A8E10>
<__main__.MyClass object at 0x036A8E10>
Đóng gói
Đóng gói là một trong những nguyên tắc cơ bản của OOP. OOP cho phép chúng tôi che giấu sự phức tạp của hoạt động bên trong của đối tượng, điều có lợi cho nhà phát triển theo những cách sau:
Đơn giản hóa và dễ hiểu khi sử dụng một đối tượng mà không cần biết nội dung bên trong.
Mọi thay đổi đều có thể dễ dàng quản lý.
Lập trình hướng đối tượng chủ yếu dựa vào tính đóng gói. Các thuật ngữ đóng gói và trừu tượng hóa (còn gọi là ẩn dữ liệu) thường được sử dụng như những từ đồng nghĩa. Chúng gần như đồng nghĩa, vì sự trừu tượng đạt được thông qua tính đóng gói.
Tính đóng gói cung cấp cho chúng ta cơ chế hạn chế quyền truy cập vào một số thành phần của đối tượng, điều này có nghĩa là không thể nhìn thấy biểu diễn bên trong của một đối tượng từ bên ngoài định nghĩa đối tượng. Quyền truy cập vào dữ liệu này thường đạt được thông qua các phương pháp đặc biệt -Getters và Setters.
Dữ liệu này được lưu trữ trong các thuộc tính cá thể và có thể được thao tác từ bất kỳ đâu bên ngoài lớp. Để bảo mật, dữ liệu đó chỉ nên được truy cập bằng các phương thức cá thể. Không được phép truy cập trực tiếp.
class MyClass(object):
def setAge(self, num):
self.age = num
def getAge(self):
return self.age
zack = MyClass()
zack.setAge(45)
print(zack.getAge())
zack.setAge("Fourty Five")
print(zack.getAge())
Đầu ra
Bạn có thể quan sát kết quả sau khi thực thi đoạn mã được đưa ra ở trên:
45
Fourty Five
Dữ liệu chỉ nên được lưu trữ nếu nó chính xác và hợp lệ, sử dụng cấu trúc xử lý Ngoại lệ. Như chúng ta thấy ở trên, không có giới hạn nào đối với người dùng nhập vào phương thức setAge (). Nó có thể là một chuỗi, một số hoặc một danh sách. Vì vậy, chúng tôi cần phải kiểm tra mã trên để đảm bảo tính chính xác của việc lưu trữ.
class MyClass(object):
def setAge(self, num):
self.age = num
def getAge(self):
return self.age
zack = MyClass()
zack.setAge(45)
print(zack.getAge())
zack.setAge("Fourty Five")
print(zack.getAge())
Init Constructor
Các __init__ method được gọi ngầm ngay khi một đối tượng của một lớp được khởi tạo, điều này sẽ khởi tạo đối tượng.
x = MyClass()
Dòng mã hiển thị ở trên sẽ tạo một thể hiện mới và gán đối tượng này cho biến cục bộ x.
Hoạt động khởi tạo, đó là calling a class object, tạo một đối tượng trống. Nhiều lớp thích tạo các đối tượng với các thể hiện được tùy chỉnh thành trạng thái ban đầu cụ thể. Do đó, một lớp có thể định nghĩa một phương thức đặc biệt có tên '__init __ ()' như được hiển thị:
def __init__(self):
self.data = []
Python gọi __init__ trong quá trình khởi tạo để xác định một thuộc tính bổ sung sẽ xảy ra khi một lớp được khởi tạo có thể đang thiết lập một số giá trị bắt đầu cho đối tượng đó hoặc đang chạy một quy trình bắt buộc trên việc khởi tạo. Vì vậy, trong ví dụ này, một thể hiện mới, được khởi tạo có thể được lấy bằng cách -
x = MyClass()
Phương thức __init __ () có thể có một hoặc nhiều đối số để linh hoạt hơn. Init là viết tắt của khởi tạo, vì nó khởi tạo các thuộc tính của cá thể. Nó được gọi là hàm tạo của một lớp.
class myclass(object):
def __init__(self,aaa, bbb):
self.a = aaa
self.b = bbb
x = myclass(4.5, 3)
print(x.a, x.b)
Đầu ra
4.5 3
Thuộc tính lớp
Thuộc tính được định nghĩa trong lớp được gọi là "thuộc tính lớp" và các thuộc tính được xác định trong hàm được gọi là "thuộc tính cá thể". Trong khi xác định, các thuộc tính này không có tiền tố tự, vì đây là thuộc tính của lớp chứ không phải của một cá thể cụ thể.
Các thuộc tính của lớp có thể được truy cập bởi chính lớp (className.attributeName) cũng như bởi các thể hiện của lớp (inst.attributeName). Vì vậy, các cá thể có quyền truy cập vào cả thuộc tính cá thể cũng như các thuộc tính lớp.
>>> class myclass():
age = 21
>>> myclass.age
21
>>> x = myclass()
>>> x.age
21
>>>
Một thuộc tính lớp có thể bị ghi đè trong một trường hợp, mặc dù nó không phải là một phương pháp tốt để phá vỡ tính đóng gói.
Có một đường dẫn tra cứu cho các thuộc tính trong Python. Đầu tiên là phương thức được định nghĩa trong lớp, và sau đó là lớp bên trên nó.
>>> class myclass(object):
classy = 'class value'
>>> dd = myclass()
>>> print (dd.classy) # This should return the string 'class value'
class value
>>>
>>> dd.classy = "Instance Value"
>>> print(dd.classy) # Return the string "Instance Value"
Instance Value
>>>
>>> # This will delete the value set for 'dd.classy' in the instance.
>>> del dd.classy
>>> >>> # Since the overriding attribute was deleted, this will print 'class
value'.
>>> print(dd.classy)
class value
>>>
Chúng tôi đang ghi đè thuộc tính lớp 'class' trong ví dụ dd. Khi nó bị ghi đè, trình thông dịch Python sẽ đọc giá trị được ghi đè. Nhưng một khi giá trị mới bị xóa bằng 'del', giá trị được ghi đè sẽ không còn xuất hiện trong trường hợp này nữa, và do đó, việc tra cứu sẽ tăng lên một mức cao hơn và lấy nó từ lớp.
Làm việc với Dữ liệu Lớp và Phiên bản
Trong phần này, chúng ta hãy hiểu cách dữ liệu lớp liên quan đến dữ liệu cá thể. Chúng ta có thể lưu trữ dữ liệu trong một lớp hoặc trong một cá thể. Khi chúng tôi thiết kế một lớp, chúng tôi quyết định dữ liệu nào thuộc về cá thể và dữ liệu nào sẽ được lưu trữ vào lớp tổng thể.
Một cá thể có thể truy cập dữ liệu lớp. Nếu chúng ta tạo nhiều cá thể, thì những cá thể này có thể truy cập các giá trị thuộc tính riêng lẻ của chúng cũng như dữ liệu lớp tổng thể.
Do đó, dữ liệu lớp là dữ liệu được chia sẻ giữa tất cả các cá thể. Hãy quan sát đoạn mã đưa ra dưới đây để biết cách đặt lệnh dưới tốt hơn -
class InstanceCounter(object):
count = 0 # class attribute, will be accessible to all instances
def __init__(self, val):
self.val = val
InstanceCounter.count +=1 # Increment the value of class attribute, accessible through class name
# In above line, class ('InstanceCounter') act as an object
def set_val(self, newval):
self.val = newval
def get_val(self):
return self.val
def get_count(self):
return InstanceCounter.count
a = InstanceCounter(9)
b = InstanceCounter(18)
c = InstanceCounter(27)
for obj in (a, b, c):
print ('val of obj: %s' %(obj.get_val())) # Initialized value ( 9, 18, 27)
print ('count: %s' %(obj.get_count())) # always 3
Đầu ra
val of obj: 9
count: 3
val of obj: 18
count: 3
val of obj: 27
count: 3
Nói tóm lại, các thuộc tính của lớp là giống nhau cho tất cả các trường hợp của lớp trong khi các thuộc tính cá thể là riêng cho mỗi trường hợp. Đối với hai trường hợp khác nhau, chúng ta sẽ có hai thuộc tính cá thể khác nhau.
class myClass:
class_attribute = 99
def class_method(self):
self.instance_attribute = 'I am instance attribute'
print (myClass.__dict__)
Đầu ra
Bạn có thể quan sát kết quả sau khi thực thi đoạn mã được đưa ra ở trên:
{'__module__': '__main__', 'class_attribute': 99, 'class_method':
, '__dict__':
, '__weakref__':
, '__doc__': None}
The instance attribute myClass.__dict__ as shown −
>>> a = myClass()
>>> a.class_method()
>>> print(a.__dict__)
{'instance_attribute': 'I am instance attribute'}