کدستانِ من

۲ مطلب در اسفند ۱۳۹۸ ثبت شده است

  • ۰
  • ۰

لیست یکی از دیتاتایپ های موجود در پایتون می باشد که با کمک آن می توان دنباله ای از داده ها را در اختیار داشت. لیست ها در پایتون دارای ترتیب هستند یعنی می توان با کمک ایندکس  به عنصرهای لیست دسترسی داشت. هدف ما در این پست این است که ببینیم چه روش هایی برای نمایش تکراری (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

  • سارا الف