کدستانِ من

۳ مطلب با کلمه‌ی کلیدی «پایتون» ثبت شده است

  • ۰
  • ۰

در بعضی زبان های برنامه نویسی مثل جاوا و ++C دسترسی به منابع با کلمات کلیدی private، public و protected کنترل می شوند. یکی از اهداف این کار محصورسازی داده ها (data encapsulation) در زبان های شی گرا است.
اعضای
public کلاس اعضایی هستند که هرچند در کلاس تعریف می شوند ولی از بیرونٍ کلاس قابل دسترسی هستند.
اعضای
private اعضایی هستند که محدود به کلاسی هستند که در آن تعریف می شوند و امکان دسترسی به آن ها از طریق بیرون کلاس وجود ندارد.
اعضای
protected نیز از خارج از کلاس قابل دسترسی نیستند ولی زیرکلاس ها اجازه ی دسترسی به آن ها را دارند.

 

در این میان زبان پایتون ساز و کار موثری برای پیاده سازی اعضای private، public و protected ندارد. پایتون با کمک روش پیشوندگذاری اعضا مکانیزم عدم دسترسی به اعضا را شبیه سازی می کند ولی در واقعیت این کار، عمل محدودکردن دسترسی را انجام نمی دهد. پایتون درعمل دسترسی به داده های private و protected را محدود نمی کند و فقط برنامه نویسان را به این کار متعهد کرده است. در واقع خود برنامه نویسان با دیدن پیشوندهای مشخص کننده ی اعضای private و protected باید از دسترسی و تغییر آن ها پرهیز کنند.

 

اعضا در پایتون به صورت پیش فرض public هستند. اعضای public از خارج از کلاس قابل دسترسی هستند.


class Foo:
    def __init__(self, x):
        self.x = x

در این مثال x یک عضو public است که از خارج از کلاس قابل دسترسی و تغییر است.


>>> foo = Foo(10)
>>> foo.x
10
>>> foo.x = 222
>>> foo.x
222

قرارداد پایتون برای تعریف اعضای protected اضافه کردن _ (زیرین خط یا underscore) است. این تعریف دسترسی به عضو را به جز برای خود کلاس و زیرکلاس های آن ممنوع می کند.


class MathTest:
    def __init__(self, a, b):
        self.first_number = a
        self.second_number = b

    def _add(self):
        return self.first_number + self.second_number

    def print_sum(self):
        sum = self._add()
        print('Sum of {} and {} is {}'.format(self.first_number, self.second_number, sum))

البته در واقعیت مثال بالا دسترسی به متد add که protected است را منع نمی کند و هنوز هم امکان انجام عملیات زیر خارج از کلاس وجود دارد!


>>> test = MathTest(10,20)
>>> test.print_sum()
Sum of 10 and 20 is 30
>>> test2 = MathTest(111,222)
>>> print(test2._add())
333

با اینحال همانطور که گفته شد پایتون برنامه نویسان مسئول را از دسترسی و تغییر اعضای protected منع کرده است و آن ها را متعهد به این کار کرده است.

 

اعضای private در پایتون با __ (دو زیرین خط یا دو underscore) تعریف می شوند. با اینکار دسترسی به آن ها خارج از کلاس ممنوع می باشد.


class Employee:
    def __init__(self, name, salary):
        self.__name = name
        self.__salary = salary

تفاوتی که این حالت با مثال پیش دارد این است که دسترسی مستقیم به متغیرهای private وجود ندارد و دسترسی زیر موجب ایجاد خطای AttributeError می شود.


>>> employee = Employee('John Doe', 20000)
>>> employee.__salary 
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: Employee instance has no attribute '__salary'

با اینحال اعضای private باز هم خارج از کلاس قابل دسترسی هستند. وجود __ این اعضا را به به متغیرهای object__variablename_ تبدیل می کند. بنابراین اعضای private را می شود به صورت زیر دید و تغییر داد هرچند باید از این کار پرهیز کرد.


>>> employee = Employee('Anna Bole', 30000)
>>> employee._Employee__salary
30000
>>> employee._Employee__salary = 44400
>>> employee._Employee__salary
44400

همانطور که گفته شد در برخی زبان های برنامه نویسی امکان تعریف اعضای private، public و protected با کلمات کلیدی وجود دارد. این کلمات کلیدی عملا دسترسی به اعضا را محدود یا از آن جلوگیری می کنند. در پایتون نیز به صورت قراردادی برای تعریف اعضای protected از _ و اعضای private از __ استفاده می شود. این تعریف های قراردادی در عمل مانع از دسترسی به اعضا نمی شوند ولی پایتون برنامه نویسان را متعهد و مسئول این کار می داند و آن ها را از دسترسی و تغییر اعضای private و protected خارج از کلاس منع می کند.

 

  • سارا الف
  • ۰
  • ۰

لیست یکی از دیتاتایپ های موجود در پایتون می باشد که با کمک آن می توان دنباله ای از داده ها را در اختیار داشت. لیست ها در پایتون دارای ترتیب هستند یعنی می توان با کمک ایندکس  به عنصرهای لیست دسترسی داشت. هدف ما در این پست این است که ببینیم چه روش هایی برای نمایش تکراری (iteration) عنصرهای لیست وجود دارد. در این پست برای نمونه از لیست های زیر استفاده شده است:


countries = [
    'China', 'United States', 'Brazil', 
    'Mexico', 'Japan', 'Vietnam', 
    'Egypt', 'France', 'Poland', 
    'Peru', 'Angola', 'Cuba'
]
populations = [
    1437667374, 330397596, 212101777, 
    128515225, 126593764, 97073748, 
    101738000, 65224185, 37861667,
    32841517, 32544138, 11328301
]

اولین راهی که به ذهن می رسد این است که از یک ایندکس برای دسترسی به عناصر لیست بهره ببریم. این عمل با کمک حلقه های while و for امکانپذیر است.


# (1)
i = 0
while i < len(countries):
    print(countries[i])
    i += 1

# (2)
for j in range(len(populations)):
    print(populations[j])

هرچند این عملیات از نظر کارکرد و نتیجه درست هستند اما استفاده از آن ها منطقی به نظر نمی رسد. در این روشِ استفاده از حلقه ها از ویژگی های خاص پایتون استفاده نشده است. در پایتون دیتاتایپ هایی مثل لیست و دیکشنری به صورت پیش فرض iterable هستند که به این معنی است که می توان با کمک حلقه ی for مستقیم به عناصر دسترسی داشت و آن ها را یکی یکی برشمرد. این حلقه دقیقا نتیجه ای برابر کد (۱) دارد ولی استفاده از آن بهتر است.


# (3)
for country in countries:
    # do something with country
    print(country)

گاهی ممکن است در برنامه ی خود وضعیتی داشته باشیم که علاوه بر عنصر به ایندکس آن ها نیاز داشته باشیم. حتی در این صورت هم استفاده از ایندکس جداگانه روش خوبی محسوب نمی شود.


# (4)
i = 1
for country in countries:
    print(i, country)
    i += 1

به جای کد (۴) و تعریف ایندکس جداگانه راه حل زیر، روش بسیار بهتری است.

# (5)
for index, country in enumerate(countries):
    print(index+1, country)

اگر بخواهیم عناصر دو لیست را تکرار کنیم هم راه حل زیر، روش خوبی نیست.


# (6)
i = 0
for i in range(len(countries)):
    country = countries[i]
    population = populations[i]
    print(country, ':', population)

به جای این روش بهتر است از راه زیر استفاده کرد.


# (7)
for country, population in zip(countries, populations):
    print(country, ':', population)

 

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

 

  • سارا الف
  • ۰
  • ۰

همانطور که می دانید Django ORM ابزاری برای ایجاد، حذف، بازیابی و به روز رسانی داده ها در دیتابیس را در اختیار ما قرار داده است. گاهی در ارتباط با دیتابیس به عملیات پیچیده تری نیاز داریم. یکی از ابزارهای در دسترس aggregate و annotate هستند که برای متراکم کردن یا خلاصه سازی اطلاعات به کار می روند. اگر درخواست های ما به دیتابیس شامل عملیاتی مثل شمردن، میانگین گیری، جمع کردن، یافتن ماکزیمم و مینیمم باشد می توان از aggregate و annotate استفاده کرد.

 

در مثال های این بخش از مدل های زیر استفاده شده است:


class Brand(models.Model):
    name = models.CharField(max_length=50)

class Product(models.Model):
    name = models.CharField(max_length=100)
    brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
    price = models.PositiveIntegerField()
    amount = models.PositiveSmallIntegerField()

اگر بخواهیم آمارگیری را از کل یک QuerySet انجام بدهیم از aggregate استفاده می کنیم. برای مثال اگر بخواهیم تعداد کل محصولات را پیدا کنیم یک روش موجود، استفاده از aggregate است.


from django.db.models import Sum

Product.objects.aggregate(Sum('amount'))
>>>{'amount__sum': 114}

همانطور که می بینید aggregate یک دیکشنری پایتون برمی گرداند. در نتیجه نمی توان روی نتیجه ی آن از متدهای کوئری ست مثل filter، values و ... استفاده کرد.

حال می خواهیم تعداد محصولات را در هر برند پیدا کنیم. در این حالت دیگر نمی خواهیم نتیجه را از کل داده ها پیدا کنیم بلکه هدف پیدا کردن نتیجه در هر گروه برندها است. در این صورت دیگر نمی توان از aggregate استفاده کرد و به جای آن باید از annotate استفاده کرد.
متد
annotate در واقع GROUP BY دیتابیس های SQL را پیاده سازی می کند. این متد برخلاف aggregate لیستی از کوئری ست ها را بر می گرداند در نتیجه می توان روی آن عملیاتی مثل filter، values، order_by و ... را انجام داد.
پیدا کردن تعداد محصولات هر برند با
SQL به شکل زیر انجام می شود:

SELECT Brand.name, SUM(Product.amount) AS Brand_sum
FROM Brand LEFT OUTER JOIN Product ON Brand.id=Product.brand
GROUP BY Brand.name;

همین دستور SQL را می توان با annotate به شکل زیر پیاده سازی کرد:
 


from django.db.models import Sum

brands = Brand.objects.annotate(brand_sum=Sum('product__amount'))
for brand in brands:
    print(brand.name, ' : ', brand.brand_sum)

که کوئری متناظر با آن در جنگو ORM به صورت زیر است:

SELECT "test_group_brand"."id", "test_group_brand"."name", SUM("test_group_product"."amount") AS "brand_sum"
FROM "test_group_brand" LEFT OUTER JOIN "test_group_product" ON ("test_group_brand"."id" = "test_group_product"."brand_id")
GROUP BY "test_group_brand"."id", "test_group_brand"."name"

 

البته مسئله ی بالا را می توان به شکل زیر هم نوشت:


from django.db.models import Sum

brands = Product.objects.values('brand__name').annotate(brand_sum=Sum('amount'))


که کوئری متناظر با آن به این صورت است:
 

SELECT "test_group_brand"."name", SUM("test_group_product"."amount") AS "brand_sum"
FROM "test_group_product" INNER JOIN "test_group_brand" ON ("test_group_product"."brand_id" = "test_group_brand"."id")
GROUP BY "test_group_brand"."name"

همانطور که گفته شد می توان متدهای کوئری ست را روی نتیجه ی annotate اعمال کرد. برای مثال اگر بخواهیم نام برندهایی که بیش از ۷۰۰ عدد موجودی دارند را مشخص کنیم از قطعه کد زیر استفاده می کنیم:

 


from django.db.models import Sum

brands = Brand.objects.annotate(brand_sum=Sum('product__amount')).filter(brand_sum__gt=700).values('name','brand_sum')

که کوئری آن برابر است با:

SELECT "test_group_brand"."name", SUM("test_group_product"."amount") AS "brand_sum"
FROM "test_group_brand" LEFT OUTER JOIN "test_group_product" ON ("test_group_brand"."id" = "test_group_product"."brand_id")
GROUP BY "test_group_brand"."id", "test_group_brand"."name"
HAVING SUM("test_group_product"."amount") > 400

  • سارا الف