Học Python: Từ Zero đến Hero (phần 2)

PHẦN 1

Iteration: Looping thông qua Data Structures

Như đã biết về Python Basics, List iteration rất đơn giản. Các developer Pythonthường sử dụng looping For như sau:

bookshelf = [
  "The Effective Engineer",
  "The 4 hours work week",
  "Zero to One",
  "Lean Startup",
  "Hooked"
]

for book in bookshelf:
    print(book)

Vậy là với mỗi quyển trên kệ, (có thể làm mọi thứ) để print nó. Đơn giản & trực quan, đó chính là Python.

Đối với hash data structure, chúng ta cũng có thể sử dụng loop for nhưng áp dụng key :

dictionary = { "some_key": "some_value" }

for key in dictionary:
    print("%s --> %s" %(key, dictionary[key]))
    
# some_key --> some_value

Với mỗi key trong dictionary , chúng ta sẽ print key đó và value tương ứng của nó.

Cách khác là sử dụng method iteritems.

dictionary = { "some_key": "some_value" }

for key, value in dictionary.items():
    print("%s --> %s" %(key, value))

# some_key --> some_value

Chúng ta đã đặt tên 2 biến số là key & value, nhưng không cần thiết, có thể đặt bất kì tên nào. Cùng xem:

dictionary_tk = {
  "name": "Leandro",
  "nickname": "Tk",
  "nationality": "Brazilian",
  "age": 24
}

for attribute, value in dictionary_tk.items():
    print("My %s is %s" %(attribute, value))
    
# My name is Leandro
# My nickname is Tk
# My nationality is Brazilian
# My age is 24

Rõ ràng, chúng ta đã sử dụng attribute như 1 tham số cho Dictionary key & nó hoạt động rất tốt. Quá tuyệt vời!

  Học Python: Từ Zero đến Hero (phần 1)

Classes & Objects

Một chút lý thuyết

Objects là đại diện cho các đối tượng trong thực tế như xe hơi, chó, xe đạp. Các objects sẽ chia sẻ 2 đặc tính chủ chốt là: data và behavior.

Xe sẽ có data, như số các bánh xe, số cửa sổ và sức chứa chỗ ngồi. Chúng sẽ phơi bày behavior: có thể nâng lên, dừng lại, hiển thị lượng nhiên liệu còn lại…

Chúng ta nhận diện data như các attributes & behavior như các methods trong lập trình hướng đối tượng.

Data → Attributes và Behavior → Methods

Và 1 Class chính là blueprint mà từ đây các objects đơn lẻ sẽ được tạo ra. Trên thực tế, chúng ta thường tìm các objects cùng loại với nhau. Như xe hơi đề có động cơ, bánh xe, cửa… và mỗi xe được build từ cùng bộ blueprints, có cùng các components.

Python Object-Oriented Programming mode: ON

Trong vai trò 1 ngôn ngữ lập trình hướng đối tượng, Python có 3 con concepts: class & object.

Một class là 1 blueprint, 1 model cho các objects của class đó.

Nói chung, 1 class chỉ là 1 model hoặc 1 cách để define attributes và behavior (như đã đề cập trong section theory). Ví dụ, 1 phương tiên di chuyển class có attributes riêng dùng để xác định liệu objects nào là các phương tiện di chuyển. Số lượng các bánh xe, loại bồn chứa, sức chứa chỗ ngồi và vận tốc maximum đều là các attributes của 1 phương tiện di chuyển.

Từ đây, chúng ta có thể xem syntax Python cho các classes:

class Vehicle:
    pass

Chúng ta define classes với 1 class statement  – và chỉ có vậy. Dễ mà, phải không?

Objects là các instances của 1 class. Chúng ta tạo 1 instance bằng cách đặt tên cho class.

car = Vehicle()
print(car) # <__main__.Vehicle instance at 0x7fb1de6c2638>

Ở đây, car là 1 object (hoặc instance) của class Vehicle.

Lưu ý rằng phương tiện di chuyển class của chúng ta có 4 attributes: số lượng bánh xe, loại bồn chứa, sức chứa chỗ ngồi và vận tốc tối đa. Chúng ta đặt tất cả những attributes này khi tạo 1 phương tiện object. Vậy nên ở đây sẽ define class để nhận data khi khởi tạo nó:

class Vehicle:
    def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity):
        self.number_of_wheels = number_of_wheels
        self.type_of_tank = type_of_tank
        self.seating_capacity = seating_capacity
        self.maximum_velocity = maximum_velocity

Chúng ta sử dụng method init, gọi là 1 constructor method. Vì vậy, khi tạo phương tiện di chuyển object, chúng ta có thể define những attributes này. Hãy tưởng tượng rằng bạn rất yêu thích loại Tesla Model S, và muốn tạo loại object này. Nó sẽ có 4 bánh xe, chạy năng lượng điện, gồm 5 ghế và vận tốc tối đa là 250km/giờ(155 mph). Tạo object như sau:

tesla_model_s = Vehicle(4, 'electric', 5, 250)

Bốn bánh + “loại bồn chứa” bằng điện + 5 ghế + vận tốc tối đa 250km/ giờ

Tất cả các attributes đã được thiết lập. Nhưng làm thế nào để tiếp cận được các giá trị của những attributes này? Chúng ta gửi 1 tin nhắn đến object hỏi về chúng, gọi nó là 1 method, là hành vi của object trên. Hãy implement nó:

class Vehicle:
    def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity):
        self.number_of_wheels = number_of_wheels
        self.type_of_tank = type_of_tank
        self.seating_capacity = seating_capacity
        self.maximum_velocity = maximum_velocity

    def number_of_wheels(self):
        return self.number_of_wheels

    def set_number_of_wheels(self, number):
        self.number_of_wheels = number

Đây là 1 implementation của 2 methods: number_of_wheels và set_number_of_wheels. Chúng ta gọi nó là getter & setter. Vì getter lấy attribute value và setter thiết lập 1 giá trị mới cho attribute đó.

Trong Python, thì sử dụng @property (decorators) để define gettersvà setters.

class Vehicle:
    def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity):
        self.number_of_wheels = number_of_wheels
        self.type_of_tank = type_of_tank
        self.seating_capacity = seating_capacity
        self.maximum_velocity = maximum_velocity

    @property
    def number_of_wheels(self):
        return self.number_of_wheels

    @number_of_wheels.setter
    def number_of_wheels(self, number):
        self.number_of_wheels = number

Và sử dụng các methods này như những attributes:

tesla_model_s = Vehicle(4, 'electric', 5, 250)
print(tesla_model_s.number_of_wheels) # 4
tesla_model_s.number_of_wheels = 2 # setting number of wheels to 2
print(tesla_model_s.number_of_wheels) # 2

Điều này có khác 1 chút với việc define methods. Các methods hoạt động như là các attributes. Ví dụ, khi set số bánh xe mới, chúng ta không áp số 2 vào thành 1 tham số mà set giá trị 2 vào number_of_wheels. Đây là 1 cách để viết code pythonic getter và setter.

Nhưng có thể sử dụng methods cho những thứ khác, như method “make_noise

class Vehicle:
    def __init__(self, number_of_wheels, type_of_tank, seating_capacity, maximum_velocity):
        self.number_of_wheels = number_of_wheels
        self.type_of_tank = type_of_tank
        self.seating_capacity = seating_capacity
        self.maximum_velocity = maximum_velocity

    def make_noise(self):
        print('VRUUUUUUUM')

Khi call method này, nó chỉ trả lại 1 string VRRRRUUUUM.

tesla_model_s = Vehicle(4, 'electric', 5, 250)
tesla_model_s.make_noise() # VRUUUUUUUM

Encapsulation: Ẩn thông tin

Encapsulation là cơ chế giới hạn khả năng tiếp cận trực tiếp đến data & methods của các objects. Nhưng cùng lúc đó, nó lại hỗ trỡ việc vận hành trên data đó (methods của các objects)

“Encapsulation được dùng để ẩn Data Members & Members function. VỚi Định nghĩa này, encapsulation đồng nghĩa là việc hiển thị nội bộ của 1 object được ẩn toàn bộ khỏi tầm nhìn của definition Đến từ Object đó” — Wikipedia

Tất cả hiển thị nội bộ của 1 object được ẩn khỏi bên ngoài. Chỉ có object đó mới tương tác được với data nội bộ của nó.

Đầu tiên, chúng ta cần phải hiểu cách hoạt động của các biến số instance public và non-public và methods.

Các biến số Instance công khai

Đối với 1 Python class, khởi tạo 1 public instance variable trong constructor method của chúng ta.

Trong method constructor:

class Person:
    def __init__(self, first_name):
        self.first_name = first_name

Ở đây, áp dụng value first_name như 1 argument đến public instance variable.

tk = Person('TK')
print(tk.first_name) # => TK

Trong class:

class Person:
    first_name = 'TK'

Không cần áp dụng first_name như 1 argument, và tất cả các instance objects sẽ có 1  class attribute được khởi tạo với TK.

tk = Person()
print(tk.first_name) # => TK

Tốt. Hiện chúng ta đã biết sử dụng public instance variables và class attributes. Điều thú vị khác nữa về phần public là có thể quản lý giá trị của biến, nghĩa là object có thể quản lý giá trị biến số của nó: các giá trị biến số Get và Set.

Nhắc đến Person, nếu muốn set giá trị khác cho biến first_name:

tk = Person('TK')
tk.first_name = 'Kaio'
print(tk.first_name) # => Kaio

Đấy, chúng ta chỉ set giá trị khác (kaio) cho biến instance first_name và nó đã cập nhật giá trị. Đơn giản vậy thôi. Vì là biến public nên sẽ làm được điều đó.

Biến Instance không công khai

“Ở đây, chúng ta không sử dụng thuật ngữ “private” vì không có attribute nào thực sự riêng tư trong Python (mà  không có 1 lượng lớn công việc không cần thiết)” — PEP 8

Giống như public instance variable , chúng ta có thể define non-public instance variable trong method constructor hoặc trong class. Điểm khác biệt syntax chính là: đối với non-public instance variables, sẽ sử dụng gạch dưới (_) trước tên variable.

“‘Các biến instance private mà không thể tiếp cận ngoài trừ lúc ở trong 1 object thì sẽ không tồn tại trong python. Tuy nhiên, có 1 quy ước được hầu hết code python theo là: 1 name prexide với 1 dấu gạch dưới (như: _spam) nên được xem như 1 phần không công khai của API (dù nó là 1 function, 1 method hay 1 data member)” — Python Software Foundation

Ví dụ:

class Person:
    def __init__(self, first_name, email):
        self.first_name = first_name
        self._email = email

Bạn có thấy biến email không? Đây là cách chúng ta define 1 non-public variable

tk = Person('TK', '[email protected]')
print(tk._email) # [email protected]

“Chúng ta có thể access & cập nhật nó. Non-public variables chỉ là 1 Quy ước và nên được xem như phần non-public của API”

Vì vậy, sử dụng 1 method cho phép define trong class definition. Cùng implement 2 methods (email và update_email) để hiểu rõ hơn:

class Person:
    def __init__(self, first_name, email):
        self.first_name = first_name
        self._email = email

    def update_email(self, new_email):
        self._email = new_email

    def email(self):
        return self._email

Bây giờ chúng ta có thể update & access non-public variables bằng những methods đó. Cùng xem:

tk = Person('TK', '[email protected]')
print(tk.email()) # => [email protected]
tk._email = '[email protected]'
print(tk.email()) # => [email protected]
tk.update_email('[email protected]')
print(tk.email()) # => [email protected]
  1. Khởi tạo 1 object mới bằng first_name TK và email [email protected]
  2. Printt email bằng cách tiếp cận non-public variable với 1 method
  3. Cố gắng set email mới ngoài class
  4. Cần phải xem non-public variable như phần non-public của API
  5. Cập nhật non-public variable bằng method instance
  6. Thành công! Chúng ta có thể cập nhật nó trong class bằng method helper

Public Method

Với public methods, chúng ta cũng có thể sử dụng ngoài class:

class Person:
    def __init__(self, first_name, age):
        self.first_name = first_name
        self._age = age

    def show_age(self):
        return self._age

Thử test xem:

tk = Person('TK', 25)
print(tk.show_age()) # => 25

Tốt, vậy là không có vấn đề gì.

Non-public Method

Nhưng với non-public methods thì không làm được điều này. Hãy implement cùng class Person nhưng bây giờ với 1 show_age non-public method bằng 1 dấu gạch dưới (_).

class Person:
    def __init__(self, first_name, age):
        self.first_name = first_name
        self._age = age

    def _show_age(self):
        return self._age

Giờ chúng ta sẽ cố gọi non-public method này với object của mình:

tk = Person('TK', 25)
print(tk._show_age()) # => 25

“Chúng ta có thể access & Update nó. Non-public methods chỉ là 1 quy ước & nên được xem như 1 phần non-public của API”

Dưới đây là ví dụ về cách sử dụng non-public methods:

class Person:
    def __init__(self, first_name, age):
        self.first_name = first_name
        self._age = age

    def show_age(self):
        return self._get_age()

    def _get_age(self):
        return self._age

tk = Person('TK', 25)
print(tk.show_age()) # => 25

Chúng ta có 1 _get_age non-public method và 1 show_age public method. Object của chúng ta có thể sử dụng  show_age (bên ngoài class) và _get_age chỉ được sử dụng bên trong class definition (trong method show_age). Nhưng 1 lần nữa, đây chỉ là vấn đề liên quan đến quy ước.

Encapsulation Summary

Với encapsulation, có thể đảm bảo rằng hiển thị nội bộ của object được ẩn.

Inheritance: các hành vi và các đặc tính

Một vài objects nào đó sẽ sở hữu vài điểm chung: behavior & characterists của chúng.

Trong lập trình hướng đối tượng, các class có thể thừa hưởng những đặc tính (data) và hành vi (methods) tương tự từ class khác.

Cùng xem 1 ví dụ khác và implement nó bằng Python.

Hãy tưởng tượng 1 chiếc xe hơi. Số lượng bánh xe, sức chứa chỗ ngồi và vận tốc tối đa là tất cả các attributes của 1 chiếc xe. Có thể nói rằng 1 class ElectricCar thừa hưởng cùng các attributes từ class Car thông dụng.

Class Car đã implement như sau:

class Car:
    def __init__(self, number_of_wheels, seating_capacity, maximum_velocity):
        self.number_of_wheels = number_of_wheels
        self.seating_capacity = seating_capacity
        self.maximum_velocity = maximum_velocity

Một khi đã khởi tạo, chúng ta có thể sử dụng tất cả instance variables đã được tạo ra. Tốt.

Trong Python, hãy áp dụng parent class vào child class như 1 tham số. Một class ElectricCar có thể kế thừa từ class Car.

class ElectricCar(Car):
    def __init__(self, number_of_wheels, seating_capacity, maximum_velocity):
        Car.__init__(self, number_of_wheels, seating_capacity, maximum_velocity)

Đơn giản vậy thôi, chúng ta không cần phải implement bất kì method nào khác, vì class nà đã có rồi (được thừa hưởng từ class Car).

my_electric_car = ElectricCar(4, 5, 250)
print(my_electric_car.number_of_wheels) # => 4
print(my_electric_car.seating_capacity) # => 5
print(my_electric_car.maximum_velocity) # => 250

Thật đẹp, đúng không?

Tổng quan

Như vậy, bài viết này đã giúp chúng ta nắm được những kiến thức Python cơ bản:

  • Cách hoạt động của các biến Python
  • Cách hoạt động của conditional statements Python
  • Cách looping Python (while & for) hoạt động
  • Cách sử dụng Lists: Collection | Array
  • Dictionary Key-Value Collection
  • Cách lặp thông qua data structures
  • Objects & Classes
  • Các attibutes như data của objects
  • Methods như hành vi của các objects
  • Sử dụng getters và setters của Python & property decorator
  • Encapsulation: ẩn thông tin
  • Inheritance: behaviors (hành vi) và characteristics (đặc tính)

Bạn có thể đọc thêm quá trình nghiên cứu lập trình của tôi ở The Renaissance Developer.

Chúc vui, đừng quên hãy tiếp tục học hỏi và luôn cố gắng coding nhé!

Xem thêm các vị trí python tuyển dụng từ công ty lớn

Nguồn: TopDev via medium.freecodecamp.com