وارد کردن کلاس‌ها

بیایید ماژولی بسازیم که تنها شامل کلاس Car باشد. اینجا به یک مشکل ظریف در نام‌گذاری برمی‌خوریم: ما قبلاً در این فصل فایلی به نام car.py داشته‌ایم امّا این ماژول هم باید car.py نام‌گذاری شود زیرا دربردارنده‌ی کدی بیانگر خودروست. این مشکل نامگذاری را این‌گونه حل می‌کنیم: کلاس Car را در ماژولی به نام car.py ذخیره می‌کنیم، سپس آن را به جای فایل car.py قبلی می‌گذاریم. از این به بعد هر برنامه‌ای که از این ماژول استفاده می‌کند به نام فایل ویژه‌تری نیاز دارد، مثلاً my_car.py. در اینجا، در car.py تنها باید کد کلاس Car را ببینیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
"""A class that can be used to represent a car."""

class Car:
    """A simple attempt to represent a car."""

    def __init__(self, make, model, year):
        """Initialize attributes to describe a car."""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        """Return a neatly formatted descriptive name."""
        long_name = f"{self.year} {self.manufacturer} {self.model}"
        return long_name.title()

    def read_odometer(self):
        """Print a statement showing the car's mileage."""
        print(f"This car has {self.odometer_reading} miles on it.")

    def update_odometer(self, mileage):
        """
        Set the odometer reading to the given value.
        Reject the change if it attempts to roll the odometer back.
        """
        if mileage >= self.odometer_reading:
            self.odometer_reading = mileage
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, miles):
        """Add the given amount to the odometer reading."""
        self.odometer_reading += miles

docstringای در سطح ماژول تعریف کرده‌ایم که به طور خلاصه محتویّات ماژول را شرح می‌دهد. شما باید برای هر ماژولی که می‌سازید یک docstring بنویسید.

اکنون فایلی جداگانه به نام my_car.py می‌سازیم. این فایل کلاس Car را وارد کرده و نمونه‌ای از آن را می‌سازد:

1
2
3
4
5
6
7
8
9
from car import Car

my_new_car = Car('audi', 'a4', 2019)

print(my_new_car.get_descriptive_name())

my_new_car.odometer_reading = 23

my_new_car.read_odometer()

عبارت import به پایتون می‌گوید که ماژول car را باز و کلاس Car را وارد کن. حال می‌توانیم از کلاس Car استفاده کنیم؛ انگار که در همین فایل تعریف شده باشد. خروجی مشابه چیزی است که قبل‌تر دیدیم:

1
2
3
2019 Audi A4

This car has 23 miles on it.

وارد کردن کلاس‌ها روش موثّری برای برنامه‌نویسی است. تصوّر کنید که در صورت آوردنِ کل کلاس Car در فایل برنامه، این فایل چه‌قدر طولانی می‌شد. زمانی که به جای این کار، کلاس را به ماژولی منتقل و سپس آن ماژول را وارد می‌کنید همان قابلیّت‌ها را در اختیار دارید امّا فایل برنامه‌ی اصلی شما تمیزتر و خواندنِ آن راحت‌تر باقی می‌ماند. همچنین شما بخش مهمّی از منطق را در فایل‌های جداگانه ذخیره می‌کنید؛ به محض این که کلاس‌هایتان مطابق آن‌چه انتظار دارید کار کردند، آن‌ها را در آن فایل‌ها رها می‌کنید و در عوض در برنامه‌ی اصلی خود روی منطقِ سطحِ بالاتر تمرکز می‌کنید.

ذخیره کردن چند کلاس در یک ماژول

شما می‌توانید هر تعداد کلاس که نیاز دارید در یک ماژول ذخیره کنید، با این حال کلاس‌های هر ماژول باید به نحوی به یکدیگر مربوط باشند. کلاس‌های Battery و ElectricCar هر دو نمایانگر خودروها هستند بنابراین آن‌ها را به ماژول car.py اضافه می‌کنیم.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"""A set of classes used to represent gas and electric cars."""

class Car:
    --snip--

class Battery:
    """A simple attempt to model a battery for an electric car."""

    def __init__(self, battery_size=70):
        """Initialize the battery's attributes."""
        self.battery_size = battery_size

    def describe_battery(self):
        """Print a statement describing the battery size."""
        print(f"This car has a {self.battery_size}-kWh battery.")

    def get_range(self):
        """Print a statement about the range this battery provides."""
        if self.battery_size == 75:
            range = 260
        elif self.battery_size == 100:
            range = 315

        print(f"This car can go about {range} miles on a full charge.")

class ElectricCar(Car):
    """Models aspects of a car, specific to electric vehicles."""

    def __init__(self, make, model, year):
        """
        Initialize attributes of the parent class.
        Then initialize attributes specific to an electric car.
        """
        super().__init__(make, model, year)
        self.battery = Battery()

اکنون می‌توانیم فایل جدیدی به نام my_electric_car.py بسازیم، کلاس ElectricCar را وارد کنیم و یک خودروی الکتریکی بسازیم:

1
2
3
4
5
6
7
8
9
from car import ElectricCar

my_tesla = ElectricCar('tesla', 'model s', 2019)

print(my_tesla.get_descriptive_name())

my_tesla.battery.describe_battery()

my_tesla.battery.get_range()

خروجی این مثال مشابه آن چیزی است که قبلاً دیده‌ایم، با این حال بیشتر منطق در یک ماژول پنهان شده است:

1
2
3
4
5
2019 Tesla Model S

This car has a 75-kWh battery.

This car can go about 260 miles on a full charge.

وارد کردن چند کلاس از یک ماژول

شما می‌توانید هر تعداد کلاسی که نیاز داشتید در یک فایل برنامه وارد کنید. اگر بخواهیم در یک فایل یک خودروی معمولی و یک خودروی الکتریکی بسازیم، باید هر دو کلاس Car و ElectricCar را وارد کنیم:

1
2
3
4
5
6
7
8
9
from car import Car, ElectricCar

my_beetle = Car('volkswagen', 'beetle', 2019)

print(my_beetle.get_descriptive_name())

my_tesla = ElectricCar('tesla', 'roadster', 2019)

print(my_tesla.get_descriptive_name())

با جدا کردن کلاس‌ها با کاما می‌توانید چند کلاس را از یک ماژول وارد کنید. پس از وارد کردن کلاس‌های ضروری این امکان را دارید که هر تعداد نمونه‌ از کلاس‌های مورد نیاز خود بسازید.

در این مثال یک خودروی معمولی Volkswagen Beetle و یک خودروی الکتریکی Tesla Roadster می‌سازیم:

1
2
3
2019 Volkswagen Beetle

2019 Tesla Roadster

وارد کردن کلّ یک ماژول

علاوه بر این روش‌ها می‌توانید کلّ یک ماژول را وارد کنید و به کلاس‌های مورد نیاز به کمک علامت نقطه دسترسی پیدا کنید. این رویکرد ساده است و به کدی منجر می‌شود که خواندن آن آسان است. از آن‌جایی‌که هر فراخوانی برای ساختن نمونه‌ی یک کلاس باید شامل نام ماژول باشد، در این روش به تداخل نام با نام‌های به کار رفته در فایل فعلی برنمی‌خورید.

وارد کلّ ماژول car و ساختن یک خودروی معمولی و الکتریکی به این شکل است:

1
2
3
4
5
6
7
8
9
import car

my_beetle = car.Car('volkswagen', 'beetle', 2019)

print(my_beetle.get_descriptive_name())

my_tesla = car.ElectricCar('tesla', 'roadster', 2019)

print(my_tesla.get_descriptive_name())

کلّ ماژول car را وارد می‌کنیم. پس از آن از طریق دستور module_name.ClassName به کلاس‌های آن دسترسی پیدا می‌کنیم. دوباره یک Volkswagen Beetle و یک Tesla Roadster می‌سازیم.

وارد کردن تمام کلاس‌های یک ماژول

شما می‌توانید تمام کلاس‌های یک ماژول را به کمک دستور زیر وارد کنید:

1
from module_name import *

این روش به دو دلیل پیشنهاد نمی‌شود. یکم، خوب است که قادر باشیم با خواندن عبارت‌های import در بالای یک فایل درکی کلی از کلاس‌هایی که برنامه استفاده می‌کند پیدا کنیم. با این رویکرد واضح نیست چه کلاس‌هایی را از ماژول گرفته‌ایم. این رویکرد همچنین ممکن است به ایجاد تداخل و سردرگمی در نام‌های درون فایل شود. اگر سهواً کلاسی هم‌نام با چیزی دیگر در فایل برنامه‌ی خود وارد کنید، ممکن است به خطاهایی بخورید که تشخیص آن‌ها مشکل باشد. اگر چه این مورد، رویکرد توصیه‌شده‌ای نیست امّا اینجا نشان داده شده چون ممکن است آن را در جایی در کد دیگران ببینید.

اگر نیاز دارید که تعداد زیادی کلاس را از ماژولی وارد کنید، بهتر است کل ماژول را وارد کرده و دستور module_name.ClassName را به کار بگیرید. هرچند نام تمام کلاس‌ها را بالای فایل خود نمی‌بینید امّا هنگامی که ماژول در جایی از برنامه استفاده شود، نام آن کاملاً مشخّص است. همچنین تداخل‌های بالقوّه‌ی نام‌گذاری که هنگام وارد کردن تمام کلاس‌های یک ماژول به آن برمی‌خورید، رخ نخواهد داد.

وارد کردن یک ماژول در ماژول دیگر

گاهی ممکن است بخواهید که کلاس‌های خود را بین چند ماژول پخش کنید تا از طولانی شدن بیش از اندازه‌ی یک فایل و ذخیره شدن چند کلاس نامربوط در یک ماژول جلوگیری کنید. هنگام ذخیره کردن کلاس‌ها در چند ماژول ممکن است دریابید که کلاسی در یک ماژول به کلاسی در ماژول دیگری وابسته است. زمانی که چنین چیزی رخ دهد می‌توانید کلاس مورد نیاز را از ماژول اوّل وارد کنید.

برای مثال کلاس Car را در یک ماژول و کلاس‌های ElectricCar و Battery را در ماژولی جدا ذخیره می‌کنیم. ماژول جدیدی به نام electric_car.py می‌سازیم و آن را جایگزینِ فایل electric_car.py که پیش از این ساخته بودیم می‌کنیم و تنها دو کلاس Battery و ElectricCar را درون آن کپی می‌کنیم:

1
2
3
4
5
6
7
8
9
"""A set of classes that can be used to represent electric cars."""

from car import Car

class Battery:
    --snip--

class ElectricCar(Car):
    --snip--

کلاس ElectricCar نیاز به دسترسی به کلاس والد خود Car دارد، بنابراین Car را مستقیماً در ماژول وارد می‌کنیم. اگر این خط را فراموش کنیم، هنگام وارد کردن ماژول electric_car اعلام خطا می‌کند. همچنین باید ماژول Car را به‌روزرسانی کنیم تا تنها شامل کلاس Car باشد:

1
2
3
4
"""A class that can be used to represent a car."""

class Car:
    --snip--

اکنون می‌توانیم از هر ماژول جداگانه کلاس‌هایی را وارد کنیم و هر نوع خودرو که خواستیم بسازیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from car import Car

from electric_car import ElectricCar

my_beetle = Car('volkswagen', 'beetle', 2019)

print(my_beetle.get_descriptive_name())

my_tesla = ElectricCar('tesla', 'roadster', 2019)

print(my_tesla.get_descriptive_name())

Car را از ماژول خود و ElectricCar را از ماژول مربوطه‌اش وارد می‌کنیم. سپس یک خودروی معمولی و یک خودروی الکتریکی می‌سازیم. هر دو نوع خودرو به درستی ساخته شده‌اند:

1
2
3
2019 Volkswagen Beetle

2019 Tesla Roadster

استفاده از نام‌های مستعار

چنان‌که در فصل ۸ دیدید هنگام استفاده از ماژول‌ها نام‌های مستعار می‌توانند به مرتب‌سازی کد پروژه‌های شما کمک کنند. می‌توانید از نام‌های مستعار هنگام وارد کردن کلاس‌ها هم استفاده کنید.

به عنوان یک مثال برنامه‌ای را در نظر بگیرید که می‌خواهید در آن تعدادی خودروی الکتریکی بسازید. نوشتن و خواندن چندباره‌ی ElectricCar ممکن است خسته‌کننده باشد. شما می‌توانید در عبارت وارد کردن نام مستعاری به ElectricCar بدهید:

1
from electric_car import ElectricCar as EC

اکنون هر جا خواستید که یک خودروی الکتریکی بسازید، می‌توانید از این نام مستعار بهره بگیرید:

1
my_tesla = EC('tesla', 'roadster', 2019)

یافتن یک جریانِ کاری برای خود

همچنان‌که می‌بینید پایتون برای ساختار دادن به کد یک پروژه‌ی بزرگ چندین گزینه پیش روی شما می‌گذارد. دانستن تمام این امکان‌ها مهم است تا بتوانید بهترین راه را برای نظم‌دهی به پروژه‌های خوب بیابید و در عین حال پروژه‌های دیگر افراد را هم درک کنید.

زمانی که تازه شروع می‌کنید ساختار کد خود را ساده نگه دارید. همه‌ی کارها را در یک فایل انجام دهید و زمانی که متوجّه شدید که همه چیز درست کار می‌کند کلاس‌ها را به ماژول‌های جداگانه منتقل کنید. اگر به چگونگی ارتباط ماژول‌ها و فایل‌ها علاقه دارید، کلاس‌های خود را در ابتدای پروژه در ماژول‌ها قرار دهید. رویکردی را بیابید که به شما امکان نوشتن کدی می‌دهد که کار می‌کند و از آن شروع کنید.

خودآزمایی

۹-۱۰. رستوران‌های واردشده: از آخرین کلاس Restaurant خود استفاده کنید و آن را در یک ماژول ذخیره کنید. فایلی جدا بسازید که Restaurant را وارد می‌کند. نمونه‌ای از این کلاس بسازید و از متدهای آن استفاده کنید تا نشان دهید که عبارت import به درستی کار می‌کند.

۹-۱۱. کلاس مدیر واردشده: از کار خود در تمرین ۹-۸ شروع کنید. کلاس‌های User، Privileges و Admin را در یک ماژول ذخیره کنید. فایلی جدا بسازید، نمونه‌ای از Admin ایجاد کنید و show_privileges() را فراخوانی کنید تا معلوم شود همه چیز درست کار می‌کند.

۹-۱۲. چند ماژول: کلاس User را در یک ماژول و دو کلاس Privileges و Admin را در ماژولی دیگر ذخیره کنید. در فایلی جدا نمونه‌ای از Admin بسازید و show_privileges() را صدا کنید تا نشان دهید که همه چیز همچنان درست کار می‌کند.