یکی از امکانات جالب در پایتون ۳.۷ به بعد اضافه شدن dataclass هست. امروز و در این پست نگاهی به این کتابخوانه میکنیم و از ماژول مورد علاقه خودم برای مدلسازی pydantic هم خواهم گفت.
کلاس داده (Dataclass)
در بسیاری از پروژه ها قالب های داده داریم مثلا شخصی که در سیستم ثبت میشود دارای فیلد های داده مانند نام، نشانی، سن، آدرس ایمیل و … است (جداً برای فیلد هیچ معادل فارسی درستی پیدا نکردم!). یکی از روش های نگهداری و استفاده از این داده ها که ساختار و فیلد های مشخصی دارند استفاده از dataclass است.
در واقع dataclass یک decorator از کتابخانهی dataclasses است. کلاس داده تفاوت یا محدودیتی نسبت به یک کلاس معمولی پایتون ندارد اما میتواند کار را سادهتر کند چون که این دکوریتور خودش توابع __init__()
و __repr__()
را ایجاد میکند.
from dataclasses import dataclass, field
@dataclass
class Person:
name:str
lastname:str
age:int = 0
address:str = ""
children: list["person"] = field(default_factory=list)
کد بالا یک مدل داده برای یک فرد را میسازد. دکوریتور dataclass همراه با توابع دیگر یک تابع init مانند زیر ایجاد میکند:
def __init__(self, name:str, lastname:str, age:int = 0, address="", children=[]):
self.name = name
self.lastname = lastname
self.age = age
...
دقت کنید که هنگام ایجاد کلاس داده استفاده از typehint ضروری است. typehint (راهنمای نوع؟!) یک شیوه نوشتار در پایتون برای اشاره به نوع داده یک متغیر است. مثلا:
age:int
فاروق در همین مورد این مطلب رو نوشته:
استفاده از تابع field در کلاس های داده
همونطور که در مثال بالا آمده برخی فیلدها میتوانند داده پیشفرض داشته باشند (در مثال age, address و children) اما باید دقت کنید که برای داده هایی که اسطلاحاً mutable هستند مانند list, dict ,… برای این که مشکلی پیش نیاد باید از تابع field استفاده کنید تا یک تابع مثلا list()
را هنگام ایجاد یک نمونه صدا بزند و خروجی را در فیلد ذخیره کند (در غیر این صورت یک لیست مشترک بین همه نمونه ها خواهید داشت که میتواند برایتان ایجاد مشکل کند).
from datetime import datetime
@dataclass
class book:
name:str
author:str
was_added_at:datetime = field(default_factory=datetime.now)
owners:list[Person] = field(default_factory=list)
در کد بالا برای فیلد «تاریخ اضافه شدن» هنگام ایجاد یک نمونه تابع now()
صدا زده میشود که تاریخ فعلی را باز میگرداند (البته اگه در ورودی ها تاریخ دیگری داده شد جایگزین میشود)
ماژول pydantic
اگر امکانات بیشتری نیاز داشتید حتما نگاهی به ماژول pydantic بیاندازید. این ماژول کامل با هدف مدلسازی داده و اعتبارسنجی داده هاست. این ماژول امکانات زیادی داره مثل خواندن و خروجی به فرمت های مختلف مثل json، تولید schema و …
جدا از این که با linter
ها و ویرایشگر های کد به خوبی کار میکند در بسیاری از ماژول های دیگر مانند FastAPI, Beanie و … استفاده شده.
from datetime import datetime
from pydantic import BaseModel, PositiveInt
class User(BaseModel):
id: int
name: str = 'John Doe'
signup_ts: datetime | None
tastes: dict[str, PositiveInt]
این ماژول علاوه بر این که همانند یک dataclass به شما امکان دسترسی به فیلد ها مختلف با نوشتار نقطهای میدهد (dot notation) و ویرایشگر کد شما نیز به خوبی شما را راهنمایی میکند، صحت داده ها را میسنجد، مثلا نمیتوانید برای فیلد id که از جنس عدد است یه متن وارد کنید. (البته pydantic دوست داشتنی، رشته "12"
را به عدد تبدیل میکنه و گیر نمیده ولی اجازه نمیده که مثلا "foobar"
ذخیره بشه)
مقایسه pydantic با dataclass
ماژول pydantic تمام نیاز هایی که هنگام استفاده از dataclass دارید را به خوبی پاسخگو هست و به شما بسیاری امکانات بهتر و مفیدتر می دهد، برای مثال تبدیل یک نمونه به JSON و بلعکس به همراه اعتبارسنجی داده ها به سادگی انجام میشود و نوع داده های مختلفی مثل آدرس ایمیل، عدد مثبت و حتی نوع داده سفارشی شما را پشتیبانی میکند. کلی امکانات دیگر نیز هستند که همهی آن ها را در مستندات کامل این ماژول در سایتشون میتونید بخونید.
اگر شماهم توی مخازن دبیانتون احساس کندی میکنید یا با ارور های متعدد 403 و 404 روبرو میشید ، این پست رو تا انتها بخونید D:
حالا این netselect چی هستش؟
از خیلی مدت پیش، کاربرای توزیع هایی مثل آرچ از ابزاری به اسم reflector استفاده می کردن. این ابزار توی ویکی آرچ هم اسمش اومده و یکی از پر استفاده ترین ها برای انتخاب سریع ترین مخزن ممکن توی آرچه.
من خودم تا مدت ها نمیدونستم که برای دبیان هم همچین چیزی وجود داره، (توی اوبونتو میشه از برنامه گرافیکی که برای مدیریت مخازن هست ، سریعترین مخزن ممکن رو انتخاب کرد. همچین برنامه ای در دبیان به صورت پیشفرض و با همون پشتیبانی وجود نداره) تا اینکه به واسطه یکم دبیان گردی با این ابزار آشنا شدم. کارش این هستش که مثل reflector سریعترین مخزن apt رو برای شما انتخاب میکنه.
نصب و راهاندازی
برای نصب netselect کافیه که دستور زیر رو داخل ترمینال اجرا کنید:
sudo apt install netselect-apt
بعد از اینکه نصبش کردین بریم تا کار رو باهاش یادبگیریم 🙂
طریقه استفاده
برای استفاده از این دستور کافیه توی ترمینال
sudo netselect-apt
رو اجرا کنیم.
حالا چه امکاناتی داره؟
تعیین نسخه / کدنیم دبیان
به عنوان مثال برای اینکه تعیین کنیم مخازنی که برای ما پیدا میکنه برای کدوم نسخه از دبیان باشه (مثلاً دبیان sid یا testing) باید جلوی دستور کدنیم دبیان روهم بنویسیم:
sudo netselect-apt stable
دستور بالا میاد و مخازن سریع رو برای دبیان پایدار پیدا میکنه.
تعیین کشور
همینطور شما میتونید توی این دستور تعریف کنید تا مخازن مخصوص یک کشور رو برای شما لیست کنه:
sudo netselect-apt -c country
برای مثال من میخوام سریعترین مخزن ممکن که داخل ایران هستش رو برای خودم تنظیم کنم:
sudo netselect-apt -c iran
یک معماری دیگه؟
بله درست خوندید D: شما میتونید با netselect حتی مخازن مخصوص یک معماری دیگه روهم دریافت کنید:
مثلا من اگه بخوام سریعترین مخزن برای معماری ۳۲ بیتی رو پیدا کنم ، باید از فلگ a- و یا arch– استفاده کنم:
sudo netselect-apt -a i386
و یا
sudo netselect-apt --arch i386
بعد از اجرای این دستور سریعترین مخزن ۳۲ بیتی برای ما انتخاب میشه.
دیگه چه فلگ هایی داره؟
یک فلگ برای تنظیم مخازن غیررایگان (non-free) و یا فعال کردن مخازن سورس (deb-src) وجود داره.
برای مثال اگه من بخوام یک مخزنی رو پیدا کنم در ایران، برای معماری ۳۲ بیتی به همراه مخازن غیررایگان و سورس باید دستور رو با فلگ های زیر اجرا کنم:
sudo netselect-apt -a i386 -c iran -n -s stable
فلگ n- برای فعال کردن مخازن non-free و فلگ s- برای فعال کردن مخازن سورس به کار میرن 🙂
همینطور اگر مایل بودید man-page این دستور رو به صورت آنلاین و یا از طریق خود ترمینال با وارد کردن دستور زیر میتونید بخونید:
man netselect-apt
سلام! جعفر هستم و این اولین نوشتهام توی وبلاگ کرم های کامپیوتره 🙂
حالا قراره درباره چی حرف بزنم؟ یک چند وقتی درگیر این بودم که لیبرهترنسلیت که یک مترجم آزاده(این) رو برای زبان فارسی بهبود بدم. یعنی کاری کنم که ترجمه بهتری برای زبان فارسی داشته باشه.
قبلش بگم که لیبره ترنسلیت چیه؟
لیبره ترنسلیت یک نرمافزار ترجمه ماشینی آزاده که بر پایه کتابخانههای آرگوز ترنسلیت(argos-translate) توسعه داده شده که با یک رابط تحت وب به صورت سرویس ترجمه ماشینی نامتمرکز در دسترسه. مثلا نمونهای که توسط توسعهدهندگانش مدیریت میشه اینه:
درباره نحوه کارکرد و زبانهایی که پشتیبانی میکنه میتونید توی صفحه گیتهابشون بخونید.
حالا من میخواستم چیکار کنم؟
با توجه به اینکه لیبره ترنسلیت یک مدل هوشمصنوعی ترجمه ماشینی(البته نه دقیقا یک مدل) هستش ساختار کلیاش اینه که با یک حجم بالایی داده ترجمه به دو زبان(مبدا مثلا فارسی و مقصد مثلا انگلیسی که دوتا مدل آموزش میبینه یکی مبدا به مقصد و یکی برعکسش) نیاز داره، که اینجا هدفمون فارسی بود. اولین کار این بود که ببینیم مشکل این کیفیت پایین ترجمه چیه. رفتم یک دوری زدم و به داده هایی که مدل فارسی باهاش آموزش دیده رسیدم. حدود پنج میلیون خط ترجمه که کیفیت جالبی هم نداشت.
راهکار این بود که داده های بهتری جمع کنیم و مدل جدید آموزش بدیم. این کار رو کردیم و دوستانی هم کمک بسیاری کردند در این مسیر و حدود ۲ میلیون خط ترجمه جمع شد(عموما سر هم کردن دیتاست های آزاد دیگه). یک دوست عزیز دیگهای لطف کردند سخت افزارشون رو برای آموزش مدل در اختیار من گذاشتن(برای آموزش اینجور مدلها به سخت افزار قوی از جمله GPU قوی نیازه که من ندارم :) و مدل رو با حدود پنج ساعت پردازش آموزش دادیم، اما خروجی از مدل لیبره ترنسلیت هم بدتر بود!
با عیبیابی به این نتیجه رسیدیم که هم کیفیت این دادهها کافی نیست و هم حجمشون. مشکل دیگه این بود که بخش قابل توجهی از دادهها ترجمه واژهنامهوار (و نه جمله در برابر جمله) بود که کار رو خراب میکرد.
یک مدتی گذشت در همین موضوع من یک برنامه ای چیدم برای حلش و یک ارائهای در دورهمیهای کرمهای کامپیوتر دادم. با این موضوع که که چه کنیم که مشکل رو حل کنیم. دوستان لطف کردن کمک کردن و همچنین حمایت بیشتر :))
خواستیم شروع کنیم من یک مقدار اسکریپت برای خودکار کردن استخراج داده، از اینترنت و تبدیلشون به ترجمه با گوگل ترنسلیت، نوشتم و یکم داده جمع شد، بعد از چند روز که داشتم توی اینترنت چرخ میزدم به این لینک رسیدم:
https://opus.nlpl.eu/NLLB/en&fa/v1/NLLB
همینجا بود که فهمیدیم این همه کار به قولی الکی بوده :/ یعنی چی؟ یعنی ما برنامه داشتیم با کلی پردازش و بازبینی انسانی ۵ میلیون خط ترجمه خوب جمع کنیم اما اینجا حجم بسیار بزرگی یعنی ۲۵ میلیون خط ترجمه انگلیسی به فارسی به حجم ۴۷ گیگابایت خوابیده بود!
حالا چرا پیگیر آموزش مدل نشدم؟
اول اینکه من سخت افزار لازم رو برای آموزش نداشتم، و اینکه اگه کسی مثل دفعه قبل زحمت آموزش رو میکشید مثلا با اون سختافزار قبل (یعنی گرافیک با ۱۲ گیگابایت حافظه ویدئویی) و اگه زمان آموزش رو خطی فرض کنیم ۶۲.۵ ساعت نیاز بود که گرافیک جون بکنه که با این حجم داده مدل رو آموزش بده! و این از توان من خارجه.
و باید چیکار کرد؟
این کار متاسفانه خارج از توان منه. اما اگر کسی بخواد این کار رو انجام بده نیاز داره به کارت گرافیک قوی و زمان برای آموزش یا اگه اینها رو نداره میشه دو سه روزی یک سرور GPU دار اجاره کنه و این کار رو انجام بده، البته کمک دیگهای برای این کار نیاز باشه من میتونم همین چند خط تجربهای که سر این موضوع کسب کردم در اختیارش بذارم 🙂
الگوریتم کولهپشتی، یک الگوریتم از نوع حریصانه هست. در این مطلب در مورد الگوریتم کولهپشتی یک و صفر (0/1 knapsack algorithm) مینویسم. برای مثال فرض کنید یک کوله پشتی با حجم ۹۰ لیتر دارید؛ و وسایلی که دارید علاوه بر حجم برای شما یک ارزش یا اهمیت دارند و احتمالا همه آنها جا نمیشوند. حال به چه صورت وسایل را انتخاب میکنید تا بیشترین ارزش را در کوله پشتی خود داشته باشید؟ در الگوریتم کولهپشتی یک و صفر یا یک شی انتخاب میشود و یا خیر! امکان برش و تکه کردن نیست.
سلام! به مسئلهای برخوردم که نیاز داشت یه آرایه با طول نامعلوم رو از کاربر بگیریم. اینطوری که کاربر شروع میکنه به وارد کردن ورودیها تا زمانی که کلمه end رو بزنه و تموم کنه. مسئله سادهای هست و در کل زیاد چیز شاخی نیست!!
چالشش اینه که ما تعداد اعضا رو نمیدونیم و طول آرایه هم ثابته کنه، پس در حالت عادی مجبوریم ورودیها رو بریزیم تو یه لیست تا آخرش که کار کاربر تموم شد همه رو مثلا منتقل کنیم به یه آرایه با طول لیسته، یعنی داریم دو بار هر عضو رو بررسی میکنیم که هزینه زمانی اینجا میشه 2n(معادل O(n)) از طرفی توی زبانی مثل C که اصلا لیست نیست و باید مثلا از SLL(به انگلیسی: Singly Linked List و به فارسی: فهرست پیوندی یکطرفه) استفاده کرد.
#include <stdio.h>
#include <stdlib.h>
double* rec(int i) {
char input[100]; // Adjust the input string size as needed
fgets(input, sizeof(input), stdin);
if (strcmp(input, "end\n") == 0) {
double* result = (double*)malloc(i * sizeof(double));
return result;
}
double* arr = rec(i + 1);
sscanf(input, "%lf", &arr[i]);
return arr;
}
int main() {
double* result = rec(0);
// Printing the result
for (int i = 0; result[i] != 0.0; ++i) {
printf("%lf ", result[i]);
}
free(result); // Free the allocated memory
return 0;
}
مشابه این کد در پایتون به این شکل نوشته میشود(برای آرایه از numpy استفاده شده).
from numpy import np
def rec(i=0):
inp = input()
if inp == "end":
return np.zeros(i)
arr = rec(i + 1)
arr[i] = int(inp)
return arr
در واقع این شیوه به شکلی هوشمندانه از پشته(به انگلیسی: stack) خود سیستم برای ذخیره موقتی اعضا و شمارش آن ها استفاده میکنه. اما باید دقت کنید که این روش نامحدود هم نیست چون به اندازه پشته محدود میشه.
روبوکد یک بازی برنامهنویسی است که در آن با زبان جاوا، اقدام به برنامهنویسی کردن روباتهای کوچکی میکنید که با بقیهٔ روباتها باید بجنگند. مبارزه میتواند به صورت تکبهتک با یک روبات دیگر یا به صورت گروهی با مثلاً ۹ روبات دیگر باشد. هرچند که باید روباتهای خود را به زبان جاوا بنویسید، اما دانش عمیقی از این زبان مورد نیاز نیست و در صورتی برنامهنویس یکی از زبانها از همین خانواده باشید، میتوانید بهراحتی یک روبات بسازید.
در این مطلب میگیم که چطور میشه در پایتون یک پروسه دیگر را اجرا کرد و خروجی استاندارد و ورودی استاندارد رو بگیریم و ازش استفاده بکنیم. خروجی و ورودی استاندارد همون چیزایی هستن که تو محیط متنی چاپ میشن یا کاربر توی ورودی برنامه وارد میکنه. در واقع توی این مطلب یاد میگیرید که چطور میتونید در پایتون با برنامه های کنسولی دیگه تعامل کنید.
پایپ (pipe) چیست؟
به طور پیشفرض سیستمعامل ورودیها رو از موس و کیبورد میگیره و خروجیها رو روی صفحهنمایش مینویسه. اما در بعضی مواقع نیاز هست که یک برنامه از خروجیهای یک برنامه (یا دستور) دیگه استفاده کنه یا به ورودی استاندارد یک برنامه داده ارسال کنه. در چنین شرایطی pipe استفاده میشه. pipe یک فضای موقتی در حافظه برای جابهجایی اطلاعات بین دو برنامه هست که البته یک طرفه هم هست؛ یعنی مثلا برای گرفتن خروجی باید از یک pipe و برای نوشتن ورودی هم از یک pipe دیگر باید استفاده کرد.
زبانها معمولاً یا تعیین نوع پویا دارند؛ مانند کامن لیسپ، پایتون، جاوا اسکریپت یا دارای تعیین نوع ایستا هستند؛ مانند سی و سیپلاسپلاس، راست و دوباره کامن لیسپ (معمولاً پیادهسازیهای مدرن کامن لیسپ، مانند SBCL، اجازه میدهند بنا به خواست برنامهنویس، قسمتی از کد، دارای تعیین نوع ایستا و قسمتی دارای تعیین نوع پویا باشد).
در پایتون، تعیین نوع متغیرها، مقدار یا مقادیر بازگشتی توابع و متدها و آرگومانهای توابع اجباری نیست. اما میتوانیم با تعیین نوع و استفاده از یک نرمافزار Linter به کاهش خطاهای خود پیش از اجرا کمک کنیم.
def f(x: int) - > int:
return x * 2 + 1
با این که تعیین نوع آرگومان یا ورودی تابع f که x باشد و مقدار بازگشت آن یعنی x*2 + 1
در اجرا تأثیری ندارد، اما زمانی که بخواهم مقدار بازگشت تابع f را با یک رشته نویسه (کاراکتر) جمع کنم به من اخطار داده میشود:
بدیهی است که اجرای این برنامه به وسیلهٔ پایتون نیز باعث خطا میشود. در مقابل زمانی که نوع ورودی و خروجی تابع را مشخص نمیکنم اخطاری داده نمیشود:
در ادامه با تعیین نوع ورودیها و خروجی توابع و متدها و ویژگیهای یک کلاس آشنا میشویم.
مسئلهٔ ۸ وزیر میپرسد که در یک صفحهٔ شطرنج چهطور میتوانیم ۸ مهرهٔ وزیر را چنان قرار دهیم که هیچکدام در معرض تهدید دیگری نباشد. در ریاضیات و علوم کامپیوتر، مسئلهٔ n وزیر یک نسخهٔ تعمیمیافته از ۸ وزیر میباشد که برای اکثر nهای صحیح مثبت (یا طبیعی) بیشتر از یک چینش وجود دارد.
قبلاً یک روش برای پیدا کردن راه حل برای مسئلهٔ ۸ وزیر با استفاده از الگوریتم ژنتیک ارائه دادم. حال میخواهم یک روش دیگر برای همین هدف اما به صورت یک الگوریتم قطعی و تصادفی به همراه کد پایتون ارائه دهم.
زمانی که از یک برنامهنویس پایتونی بخواهید یک تابع ساده بنویسد تا ارقام یک عدد را جمع کند و برگرداند، احتمالاً ابتدا عدد را به یک رشته تبدیل میکند و سپس رشته را به لیست (فهرست) و نهایتاً تکتک اعضای لیست را که ارقام عدد به صورت رشتههای تکنویسهای هستند به عدد تبدیل میکند و سپس آنها را با هم جمع میکند. ولی الزاماً اولین روشی که برای حل مسئله به ذهنمان میرسد، بهترین روش نیست. در این مطلب دو الگوریتم برای جمع ارقام یک عدد صحیح به صورت بازگشتی و تکرارشونده به همراه کد های پایتون آن ها ارائه میدهم.
صدای بوق ممتد از لحاظ فنی همان موج سینوسی با فرکانسی (بسامد) ثابت است. در این مطلب یک کد ساده و کوتاه پایتون ارائه میدهم که بدون استفاده از کتابخانههای اضافی، میتواند یک بوق را با هر فرکانسی تولید کند و در یک فایل صوتی wave ذخیره کند.
مسئلهٔ ۸ وزیر یک تمرین معروف در علوم کامپیوتر و ریاضیات میباشد. این مسئله در مورد یک صفحهٔ شطرنج رایج و مهرهٔ وزیر در این بازی فکری میباشد. در ریاضیات ثابت میشود که میتوان ۸ وزیر را در یک صفحهٔ شطرنج چنان قرار داد که هیچکدام از وزیرها، دیگری را تهدید نکنند. با تعمیم این مسئله در ریاضیات، ثابت میشود که در یک صفحهٔ شطرنج به ضلع n، میتوان تعداد n وزیر قرار داد؛ چنانچه هیچکدام دیگری را تهدید نکنند. در علوم کامپیوتر میتوان با روشهای مختلفی این مسئله را حل کرد و به یک چینش از مهرههای وزیر رسید که هیچکدام دیگری را تهدید نکنند. یکی از این روشها «الگوریتم ژنتیک» است.
خیلی ها میگن پایتون زبان سادهای هست، میشه اون رو زود یاد گرفت، بخاطر تعیین نوع پویا(dynamic typing) دیگه نیازی نیست که برنامهنویس با type ها درگیر باشه و …
ولی آیا واقعا همینطوره؟! توی این مطلب قصد دارم کمی زبان پایتون رو به عنوان یه برنامهنویس پایتون بررسی کنم.
۱. پایتون زبان سادهای هست
منظور از سادگی زبان میتونه خوانا بودن اون و نزدیکی اون به زبان انگلیسی (زبان انسان) باشه. برای مثال دو تکه کد زیر رو ببینید:
زبان پایتون:
if not 2 in lst:
for x in lst:
print(x);
زبان سیشارپ:
if ( ! lst.contains(2) )
foreach ( int x in lst )
Console.WriteLine(x);