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

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

کلاس Car

بیایید کلاس جدیدی بنویسیم که نمایانگر یک خودرو باشد. کلاس ما اطّلاعاتی درباره‌ی نوع خودرویی که با آن کار می‌کنیم ذخیره می‌کند و متدی دارد که این اطّلاعات را خلاصه می‌کند:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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

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

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

print(my_new_car.get_descriptive_name())

متد __init__() را با پارامتر ابتدایی self تعریف می‌کنیم. همچنین سه پارامتر دیگر به آن می‌دهیم: make، model و year. متد __init__() این پارامترها را می‌گیرد و آن‌ها را به مشخّصه‌هایی که با نمونه‌های ساخته‌شده از این کلاس در ارتباطند، نسبت می‌دهد. زمانی که نمونه‌ی جدیدی از کلاس Car می‌سازیم باید نام شرکت سازنده، مدل و سال ساخت آن را برای نمونه‌ی خود تعیین کنیم.

متدی به نام get_descriptive_name() تعریف می‌کنیم که year، make و model را در یک رشته قرار می‌دهد که خودرو را با سادگی و آراستگی توصیف می‌کند. این متد ما را از چاپ کردن جداگانه‌ی مقدار هر مشخّصه خلاص می‌کند. برای کار کردن با مقادیر مشخّصه‌های در این متد از self.make، self.model و self.year استفاده می‌کنیم. نمونه‌ای از کلاس Car می‌سازیم و آن را به متغیّر my_new_car نسبت می‌دهیم. سپس get_descriptive_name() را فراخوانی می‌کنید تا نوع خودروی ما را مشخّص کند:

1
2019 Audi A4

برای جالب‌تر کردن کلاس، مشخّصه‌ای به آن اضافه می‌کنیم که با زمان تغییر کند. این مشخّصه مسافت پیموده شده‌ی کلّی خودرو را ذخیره می‌کند.

تعیین کردن مقداری پیش‌فرض برای یک مشخّصه

می‌توان هنگامِ ساختنِ یک نمونه، مشخّصه‌ای را بدون انتقال آن به صورت پارامتر تعریف کرد. این مشخّصه‌ها می‌توانند در متد __init__() تعریف شوند و در آن‌جا به آن‌ها مقداری پیش‌فرض داده شود.

مشخّصه‌ای به نام odometer_reading اضافه می‌کنیم که همیشه در ابتدا مقدار ۰ دارد. همچنین متدی به نام read_odometer() اضافه می‌کنیم که ما را در خواندن مقدار کیلومترشمار هر خودرو کمک می‌کند:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class 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):
        --snip

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

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

print(my_new_car.get_descriptive_name())

my_new_car.read_odometer()

این بار پایتون برای ساختن نمونه‌ی جدید متد __init__() را فرامی‌خواند که مقدار سازنده، مدل و سال را مانند مثال قبلی به عنوان مشخّصه ذخیره می‌کند. سپس پایتون مشخّصه‌ای جدید به نام odometer_reading می‌سازد و به آن مقدارِ اوّلیّه‌ی ۰ می‌دهد. همچنین متد جدیدی به نام read_odometer() دارید که خواندن کارکرد خودرو را ساده می‌کند.

کارکرد خودروی ما در ابتدا ۰ است:

1
2
3
2019 Audi A4

This car has 0 miles on it.

تمام خودروها با کارکرد اوّلیّه‌ی دقیقاً ۰ فروخته نمی‌شوند پس نیاز به روشی داریم تا مقدار این مشخّصه را تغییر دهیم.

تغییردادن مقدار مشخّصه‌ها

مقدار یک مشخّصه را می‌توانید به سه روش عوض کنید: تغییر دادن مستقیم از طریق یک نمونه، تعیین کردن مقدار از طریق یک متد یا افزایش دادن مقدار (افزودن مقداری به آن) به کمک یک متد. به هر یک از این رویکردها نگاهی می‌اندازیم.

تغییر دادن مستقیمِ مقدار یک مشخّصه

ساده‌ترین راه برای تغییرِ مقدارِ یک مشخّصه این است که از طریق یک نمونه به طور مستقیم به آن دسترسی پیدا کنیم. اینجا مقدار کیلومترشمار را به طور مستقیم ۲۳ قرار می‌دهیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Car:
    --snip--

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()

از نماد نقطه برای دسترسی به مشخّصه‌ی odometer_reading خودرو استفاده کرده و مقدار آن را به طور مستقیم تعیین می‌کنیم. این خط به پایتون می‌گوید که نمونه‌ی my_new_car را بگیر، مشخّصه‌ی odometer_reading مربوط به آن را پیدا کن و مقدار آن را برابر با ۲۳ بگذار:

1
2
3
2019 Audi A4

This car has 23 miles on it.

گاهی می‌خواهید مانندِ اینجا به مشخّصه‌ها به طور مستقیم دسترسی پیدا کنیم امّا برخی اوقات متدی می‌نویسید که مقدار را برای شما به‌روز کند.

تغییر دادن مقدار یک مشخّصه از طریق یک متد

داشتنِ متدهایی برای به‌روزرسانی مشخّصه‌های خاصّی می‌تواند سودمند باشد. می‌توانید به جای دسترسی مستقیم به مشخّصه، مقدار جدید را به متدی بدهیم که به صورت داخلی این به‌روزرسانی را انجام می‌دهد.

مثالِ اینجا متدی به نام update_odometer() را نشان می‌دهد:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Car:
    --snip--

    def update_odometer(self, mileage):
        """Set the odometer reading to the given value."""
        self.odometer_reading = mileage

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

print(my_new_car.get_descriptive_name())

my_new_car.update_odometer(23)

my_new_car.read_odometer()

تنها تغییرِ ایجادشده در Car افزوده شدنِ update_odometer() است. این متد مقدار کارکرد را گرفته و به مشخّصه‌ی self.odometer_reading نسبت می‌دهد. update_odometer() را فراخوانی می‌کنیم و مقدار ۲۳ را به عنوان نشان‌وند به آن می‌دهیم (که به پارامتر mileage در تعریفِ متد مربوط است.) این متد مقدار کیلومترشمار را ۲۳ می‌کند و read_odometer() این مقدار را چاپ می‌کند:

1
2
3
2019 Audi A4

This car has 23 miles on it.

می‌توانیم متد update_odometer() را طوری گسترش دهیم که در هر بار تغییرِ مقدارِ کیلومترشمار، کاری انجام دهد. اجازه دهید منطقی کوچکی اضافه کنیم تا مطمئن شویم هیچ‌کس مقدار کیلومترشمار را به عقب برنمی‌گرداند:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Car:
    --snip--

    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!")

اکنون update_odometer() پیش از تغییر دادنِ مشخّصه، مقدار جدید را بررسی می‌کند. اگر کارکرد جدید، mileage، بزرگتر یا مساوی کارکرد فعلی (self.odometer_reading) باشد، می‌توانید مقدار فعلی کیلومترشمار را به مقدار جدید به‌روزرسانی کنید. اگر مقدار جدید کمتر از مقدار فعلی باشد، اخطاری دریافت می‌کنید مبنی بر این که نمی‌توانید مقدار کیلومترشمار را به عقب برگردانید.

افزایش مقدار مشخّصه از طریق یک متد

گاهی می‌خواهید به جای تعیین یک مقدار کاملاً جدید برای یک مشخّصه، مقدار آن را به میزان مشخّصی افزایش دهید. فرض کنید خودرویی کارکرده خریده‌ایم و در فاصله‌ی بین خریدن و سند زدن، ۱۰۰ مایل کار کرده است. متدی که اینجاست مقدار افزایشی را دریافت کرده و آن را به مقدار فعلی کیلومترشمار اضافه می‌کند:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Car:
    --snip--

    def update_odometer(self, mileage):
        --snip--

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

my_used_car = Car('subaru', 'outback', 2015)

print(my_used_car.get_descriptive_name())

my_used_car.update_odometer(23_500)

my_used_car.read_odometer()

my_used_car.increment_odometer(100)

my_used_car.read_odometer()

متد جدید increment_odometer() تعداد مایل پیموده‌شده را گرفته و آن را به self.odometer_reading اضافه می‌کند. یک خودروی کارکرده، my_used_car، ایجاد می‌کنیم. مقدار کیلومترشمار آن را با فراخوانی update_odometer() و انتقال مقدار 23_500 به آن، ۲۳،۵۰۰ می‌گذاریم. سپس increment_odometer() را صدا کرده و مقدار 100 را به آن می‌دهیم تا ۱۰۰ مایل پیموده‌شده بین خریدن و ثبت خودرو را اضافه کند:

1
2
3
4
5
2015 Subaru Outback

This car has 23500 miles on it.

This car has 23600 miles on it.

می‌توانیم به سادگی این متد را تغییر دهیم تا افزودن مقدارهای منفی را نپذیرد تا هیچ‌کس نتواند از این تابع برای کم کردن مقدار کیلومترشمار استفاده کند.

نکته
این که کاربرانِ برنامه‌ی شما چگونه مقدارهایی نظیر مقدار کیلومترشمار را به‌روزرسانی می‌کنند، به کمک چنین متدهایی قابل کنترل است امّا هرکس که به برنامه دسترسی داشته باشد می‌تواند این مقدار را با دسترسی مستقیم به مشخّصه‌ها مستقیماً دستکاری کند. امنیّت موثّر علاوه بر بررسی‌های ابتدایی مانند بالا، توجّه بسیار بیشتری به جزئیات می‌طلبد.

خودآزمایی

۹-۴. تعداد مشتری‌ها: از برنامه‌ی خود در تمرین ۹-۱ شروع کنید. مشخّصه‌ای به نام number_served با مقدار پیش‌فرض ۰ اضافه کنید. نمونه‌ای به نام restaurant از این کلاس بسازید. تعداد مشتری‌هایی را که رستوران پذیرای آن‌ها بوده چاپ کنید و سپس با تغییر این مقدار آن را دوباره چاپ کنید.

متدی به نام set_number_served() اضافه کنید که امکان تعیین تعداد مشتری‌ها را فراهم می‌آورد. این متد را با عدد جدیدی فراخوانی کنید و مقدار را دوباره چاپ کنید.

متدی به نام increment_number_served() اضافه کنید که به شما امکان افزایش تعداد مشتری‌ها را می‌دهد. این متد را با هر عددی که دوست دارید فرابخوانید که بیانگر تعداد مهمان‌های پذیرفته‌شده در یک روز کاری باشد.

۹-۵. تعداد تلاش‌ها برای ورود: مشخّصه‌ای به نام login_attempts به کلاس User در تمرین ۹-۳ اضافه کنید. متدی به نام increment_login_attempts() بنویسید که مقدار login_attempts را یکی افزایش می‌دهد. متد دیگری به نام reset_login_attempts() بنویسید که مقدار login_attempts را صفر می‌کند.

نمونه‌ای از کلاس User بسازید و increment_login_attempts() را چندین بار صدا کنید. مقدار login_attempts را چاپ کنید تا مطمئن شوید مقدار آن افزایش یافته است و سپس reset_login_attempts() را فراخوانی کنید. مقدار login_attempts را بار دیگر چاپ کنید تا مطمئن شوید مقدار آن صفر شده است.