Bài 9.4. Python - Import lớp

Khi bạn thêm nhiều chức năng vào các lớp của mình, các tệp của bạn có thể trở nên dài, ngay cả khi bạn sử dụng kế thừa và composition đúng cách. Để giữ cho các tệp của bạn gọn gàng nhất có thể, Python cho phép bạn lưu trữ các lớp trong các module và sau đó nhập các lớp bạn cần vào chương trình chính của mình.

Nhập một lớp đơn lẻ

Hãy tạo một module chỉ chứa lớp Car. Điều này đưa ra một vấn đề nhỏ về đặt tên: chúng ta đã có một tệp tên là car.py trong chương này, nhưng module này nên được đặt tên là car.py vì nó chứa mã đại diện cho một chiếc xe. Chúng ta sẽ giải quyết vấn đề đặt tên này bằng cách lưu trữ lớp Car trong một module tên là car.py, thay thế tệp car.py mà chúng ta đã sử dụng trước đó. Từ bây giờ, bất kỳ chương trình nào sử dụng module này sẽ cần một tên tệp cụ thể hơn, chẳng hạn như my_car.py. Dưới đây là car.py chỉ với mã từ lớp Car:

# filepath: /f:/blog/python-vy/9/car.py
"""Một lớp có thể được sử dụng để đại diện cho một chiếc xe hơi."""

class Car:
    """Một nỗ lực đơn giản để đại diện cho một chiếc xe hơi."""

    def __init__(self, make, model, year):
        """Khởi tạo các thuộc tính để mô tả một chiếc xe hơi."""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        """Trả về một tên mô tả được định dạng gọn gàng."""
        long_name = f"{self.year} {self.make} {self.model}"
        return long_name.title()

    def read_odometer(self):
        """In ra một câu cho biết số dặm của xe."""
        print(f"This car has {self.odometer_reading} miles on it.")

    def update_odometer(self, mileage):
        """
        Đặt giá trị của odometer_reading thành giá trị được cung cấp.
        Từ chối thay đổi nếu nó cố gắng quay ngược giá trị của odometer.
        """
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """Thêm số lượng được cung cấp vào giá trị của odometer_reading."""
        self.odometer_reading += miles

Chúng ta bao gồm một docstring cấp module mô tả ngắn gọn nội dung của module này. Bạn nên viết một docstring cho mỗi module bạn tạo.

Bây giờ chúng ta tạo một tệp riêng gọi là my_car.py. Tệp này sẽ nhập lớp Car và sau đó tạo một thể hiện từ lớp đó:

# filepath: /f:/blog/python-vy/9/my_car.py
from car import Car

my_new_car = Car('audi', 'a4', 2024)
print(my_new_car.get_descriptive_name())

my_new_car.odometer_reading = 23
my_new_car.read_odometer()

Lệnh nhập from car import Car yêu cầu Python mở module car và nhập lớp Car. Bây giờ chúng ta có thể sử dụng lớp Car như thể nó được định nghĩa trong tệp này. Đầu ra giống như chúng ta đã thấy trước đó:

2024 Audi A4
This car has 23 miles on it.

Nhập các lớp là một cách lập trình hiệu quả. Hãy tưởng tượng tệp chương trình này sẽ dài như thế nào nếu toàn bộ lớp Car được bao gồm. Khi bạn di chuyển lớp vào một module và nhập module, bạn vẫn có được tất cả các chức năng tương tự, nhưng bạn giữ cho tệp chương trình chính của mình sạch sẽ và dễ đọc. Bạn cũng lưu trữ hầu hết logic trong các tệp riêng biệt; một khi các lớp của bạn hoạt động như bạn muốn, bạn có thể để các tệp đó yên và tập trung vào logic cấp cao hơn của chương trình chính của bạn.

Lưu trữ nhiều lớp trong một module

Bạn có thể lưu trữ bao nhiêu lớp tùy thích trong một module, mặc dù mỗi lớp trong một module nên có liên quan đến nhau. Các lớp BatteryElectricCar đều giúp đại diện cho xe hơi, vì vậy hãy thêm chúng vào module car.py.

# filepath: /f:/blog/python-vy/9/car.py
"""Một tập hợp các lớp được sử dụng để đại diện cho xe hơi chạy xăng và xe điện."""

class Car:
    # ...existing code...

class Battery:
    """Một nỗ lực đơn giản để mô hình hóa một pin cho xe điện."""

    def __init__(self, battery_size=40):
        """Khởi tạo các thuộc tính của pin."""
        self.battery_size = battery_size

    def describe_battery(self):
        """In ra một câu mô tả kích thước pin."""
        print(f"This car has a {self.battery_size}-kWh battery.")

    def get_range(self):
        """In ra một câu về phạm vi mà pin này cung cấp."""
        if self.battery_size == 40:
            range = 150
        elif self.battery_size == 65:
            range = 225
        print(f"This car can go about {range} miles on a full charge.")

class ElectricCar(Car):
    """Mô hình hóa các khía cạnh của một chiếc xe hơi, cụ thể là xe điện."""

    def __init__(self, make, model, year):
        """
        Khởi tạo các thuộc tính của lớp cha.
        Sau đó khởi tạo các thuộc tính cụ thể cho xe điện.
        """
        super().__init__(make, model, year)
        self.battery = Battery()

Bây giờ chúng ta có thể tạo một tệp mới gọi là my_electric_car.py, nhập lớp ElectricCar và tạo một chiếc xe điện:

# filepath: /f:/blog/python-vy/9/my_electric_car.py
from car import ElectricCar

my_leaf = ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())
my_leaf.battery.describe_battery()
my_leaf.battery.get_range()

Điều này có cùng đầu ra mà chúng ta đã thấy trước đó, mặc dù hầu hết logic được ẩn trong một module:

2024 Nissan Leaf
This car has a 40-kWh battery.
This car can go about 150 miles on a full charge.

Nhập nhiều lớp từ một module

Bạn có thể nhập bao nhiêu lớp tùy thích vào một tệp chương trình. Nếu chúng ta muốn tạo một chiếc xe thông thường và một chiếc xe điện trong cùng một tệp, chúng ta cần nhập cả hai lớp, CarElectricCar:

# filepath: /f:/blog/python-vy/9/my_cars.py
from car import Car, ElectricCar

my_mustang = Car('ford', 'mustang', 2024)
print(my_mustang.get_descriptive_name())

my_leaf = ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())

Bạn nhập nhiều lớp từ một module bằng cách ngăn cách mỗi lớp bằng dấu phẩy. Một khi bạn đã nhập các lớp cần thiết, bạn có thể tạo bao nhiêu thể hiện của mỗi lớp tùy thích.

Trong ví dụ này, chúng ta tạo một chiếc Ford Mustang chạy xăng và sau đó là một chiếc Nissan Leaf chạy điện:

2024 Ford Mustang
2024 Nissan Leaf

Nhập toàn bộ module

Bạn cũng có thể nhập toàn bộ module và sau đó truy cập các lớp bạn cần bằng cách sử dụng ký hiệu dấu chấm. Cách tiếp cận này đơn giản và dẫn đến mã dễ đọc. Vì mỗi lệnh gọi tạo một thể hiện của một lớp bao gồm tên module, bạn sẽ không gặp xung đột tên với bất kỳ tên nào được sử dụng trong tệp hiện tại.

Dưới đây là cách nhập toàn bộ module car và sau đó tạo một chiếc xe thông thường và một chiếc xe điện:

# filepath: /f:/blog/python-vy/9/my_cars.py
import car

my_mustang = car.Car('ford', 'mustang', 2024)
print(my_mustang.get_descriptive_name())

my_leaf = car.ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())

Đầu tiên chúng ta nhập toàn bộ module car. Sau đó, chúng ta truy cập các lớp cần thiết thông qua cú pháp module_name.ClassName. Chúng ta lại tạo một chiếc Ford Mustang và một chiếc Nissan Leaf.

Nhập tất cả các lớp từ một module

Bạn có thể nhập mọi lớp từ một module bằng cách sử dụng cú pháp sau:

from module_name import *

Phương pháp này không được khuyến nghị vì hai lý do. Đầu tiên, rất hữu ích khi có thể đọc các lệnh nhập ở đầu tệp và có một cái nhìn rõ ràng về các lớp mà chương trình sử dụng. Với cách tiếp cận này, không rõ ràng bạn đang sử dụng các lớp nào từ module. Cách tiếp cận này cũng có thể dẫn đến nhầm lẫn với các tên trong tệp. Nếu bạn vô tình nhập một lớp có cùng tên với một cái gì đó khác trong tệp chương trình của bạn, bạn có thể tạo ra các lỗi khó chẩn đoán. Tôi hiển thị điều này ở đây vì mặc dù nó không phải là một cách tiếp cận được khuyến nghị, bạn có thể thấy nó trong mã của người khác vào một lúc nào đó.

Nếu bạn cần nhập nhiều lớp từ một module, bạn nên nhập toàn bộ module và sử dụng cú pháp module_name.ClassName. Bạn sẽ không thấy tất cả các lớp được sử dụng ở đầu tệp, nhưng bạn sẽ thấy rõ ràng nơi module được sử dụng trong chương trình. Bạn cũng sẽ tránh được các xung đột tên tiềm ẩn có thể phát sinh khi bạn nhập mọi lớp trong một module.

Nhập một module vào một module

Đôi khi bạn sẽ muốn phân tán các lớp của mình qua nhiều module để giữ cho bất kỳ tệp nào không trở nên quá lớn và tránh lưu trữ các lớp không liên quan trong cùng một module. Khi bạn lưu trữ các lớp của mình trong nhiều module, bạn có thể thấy rằng một lớp trong một module phụ thuộc vào một lớp trong một module khác. Khi điều này xảy ra, bạn có thể nhập lớp cần thiết vào module đầu tiên.

Ví dụ, hãy lưu trữ lớp Car trong một module và các lớp ElectricCarBattery trong một module riêng biệt. Chúng ta sẽ tạo một module mới gọi là electric_car.py - thay thế tệp electric_car.py mà chúng ta đã tạo trước đó - và chỉ sao chép các lớp BatteryElectricCar vào tệp này:

# filepath: /f:/blog/python-vy/9/electric_car.py
"""Một tập hợp các lớp có thể được sử dụng để đại diện cho xe điện."""

from car import Car

class Battery:
    # ...existing code...

class ElectricCar(Car):
    # ...existing code...

Lớp ElectricCar cần truy cập vào lớp cha của nó là Car, vì vậy chúng ta nhập Car trực tiếp vào module. Nếu chúng ta quên dòng này, Python sẽ báo lỗi khi chúng ta cố gắng nhập module electric_car. Chúng ta cũng cần cập nhật module Car để nó chỉ chứa lớp Car:

# filepath: /f:/blog/python-vy/9/car.py
"""Một lớp có thể được sử dụng để đại diện cho một chiếc xe hơi."""

class Car:
    # ...existing code...

Bây giờ chúng ta có thể nhập từ mỗi module riêng biệt và tạo bất kỳ loại xe nào chúng ta cần:

# filepath: /f:/blog/python-vy/9/my_cars.py
from car import Car
from electric_car import ElectricCar

my_mustang = Car('ford', 'mustang', 2024)
print(my_mustang.get_descriptive_name())

my_leaf = ElectricCar('nissan', 'leaf', 2024)
print(my_leaf.get_descriptive_name())

Chúng ta nhập Car từ module của nó và ElectricCar từ module của nó. Sau đó, chúng ta tạo một chiếc xe thông thường và một chiếc xe điện. Cả hai xe đều được tạo đúng cách:

2024 Ford Mustang
2024 Nissan Leaf

Sử dụng bí danh

Như bạn đã thấy trong Chương 8, bí danh có thể rất hữu ích khi sử dụng các module để tổ chức mã của dự án của bạn. Bạn cũng có thể sử dụng bí danh khi nhập các lớp.

Làm ví dụ, hãy xem xét một chương trình mà bạn muốn tạo một loạt các xe điện. Có thể sẽ rất tẻ nhạt khi gõ (và đọc) ElectricCar lặp đi lặp lại. Bạn có thể đặt bí danh cho ElectricCar trong lệnh nhập:

from electric_car import ElectricCar as EC

Bây giờ bạn có thể sử dụng bí danh này bất cứ khi nào bạn muốn tạo một chiếc xe điện:

my_leaf = EC('nissan', 'leaf', 2024)

Bạn cũng có thể đặt bí danh cho một module. Dưới đây là cách nhập toàn bộ module electric_car bằng cách sử dụng một bí danh:

import electric_car as ec

Bây giờ bạn có thể sử dụng bí danh module với tên lớp đầy đủ:

my_leaf = ec.ElectricCar('nissan', 'leaf', 2024)

Tìm quy trình làm việc của riêng bạn

Như bạn có thể thấy, Python cung cấp cho bạn nhiều tùy chọn về cách cấu trúc mã trong một dự án lớn. Điều quan trọng là biết tất cả các khả năng này để bạn có thể xác định cách tốt nhất để tổ chức các dự án của mình cũng như hiểu các dự án của người khác.

Khi bạn mới bắt đầu, hãy giữ cấu trúc mã của bạn đơn giản. Hãy thử làm mọi thứ trong một tệp và di chuyển các lớp của bạn vào các module riêng biệt khi mọi thứ đã hoạt động. Nếu bạn thích cách các module và tệp tương tác, hãy thử lưu trữ các lớp của bạn trong các module khi bạn bắt đầu một dự án. Tìm một cách tiếp cận cho phép bạn viết mã hoạt động, và từ đó phát triển.

Làm thêm nhiều bài tập miễn phí tại đây

Comments

There are no comments at the moment.