| کانال توسعه‌دهندگان پایتون |
6.62K subscribers
38 photos
2 videos
4 files
43 links
⭕️ کانال توسعه‌دهندگان پایتون دولوپیکس

💠 دولوپیکس | جامعه توسعه‌دهندگان ایرانی

💎 @Developix
🚀 Developix.ir

📌 پشتیبانی و تبلیغات:
@DevelopixSupport
Download Telegram
⭕️ Binary Searching

🔻 یک الگوریتم جستجوی سریع در لیست های مرتب شده است که در آن به‌جای اینکه تمام آیتم های یک لیست را بررسی کنیم، فقط نصف آیتم‌ها را بررسی می کنیم تا آیتم موردنظر را پیدا کنیم.

🔻 این الگوریتم فقط مختص پایتون نیست و در سایر زبان ها نیز قابل پیاده‌سازی است. نکته‌ای که وجود دارد این است که حتما باید لیست sort (مرتب) شده باشد و در غیر اینصورت این متد کاربردی ندارد.

🔷 در حالت عادی برای بررسی وجود داشتن یا نداشتن یک آیتم در یک لیست (غیر از متدهایی که وجود دارد) روی لیست حلقه for پیاده سازی می‌کنید و تمام موارد موجود داخل لیست بررسی می‌شود که در این حالت روند به کندی پیش میرود و البته دیتاهایی که معمولا ما با آن‌ها سروکار داریم آن‌چنان بزرگ نیستند ولی برای دیتاهای خیلی بزرگ این روش اصلا بهینه نیست، چون تمام آیتم‌ها را بررسی می‌کند و اگر این لیست یک میلیارد آیتم داشته باشد و آیتم مورد نظر ما دقیقا وسط لیست قرار گرفته باشد باید 500 میلیون آیتم بررسی کند تا به مقدار درست برسد که این اصلا بهینه نیست و performance را به شدت پایین می‌آورد. حتی استفاده از متدهایی که وجود دارد برای این کار با این حجم از دیتا اصلا کار درستی نیست و به شدت روی performance تاثیر می‌گذارد و اگر این حالت به صورت binary searching پیاده سازی کنید دقیقا از وسط شروع می‌کند و خیلی سریع‌تر به جواب می‌رسید.

🔴 نحوه استفاده از Binary searching:

🔸 ابتدا سه متغیر تعریف می کنیم.
first = 0 # نقطه شروع لیست
last = len(lys)-1 # نقطه پایان لیست
index = -1 # ایندکس آیتم در صورت وجود، که پیش فرض وجود نداره

🔸 سپس یک حلقه بی‌نهایت ایجاد می‌کنیم و تا زمانی که first از last کمتر و مساوی باشد و مقدار index برابر -1 باشد این حلقه اجرا می‌شود و زمانی که index مقدارش عوض شود یعنی آیتم پیدا شده و نیاز به ادامه نیست.
while (first <= last) and (index == -1)

🔸 سپس مقدار first + last و تقسیم به 2 بدون قسمت اعشار که با این کار به جای اینکه از ابتدای لیست شروع کند از وسط لیست شروع می کند.

mid = (first + last) // 2 

if lys[mid] == val:
index = mid

elif val < lys[mid]:
last = mid - 1

else:
first = mid + 1

🔸 در شرط‌های ایجاد شده بررسی می کنیم که مقدار اگر برابر با val بود، index برابر با mid شود. اگر val کمتر از مقدار آن ایندکس بود، مقدار last برابر با mid - 1 شود. و اگر مقدار val بیشتر از آن بود، first برابر با mid + 1 شود. با این روش فقط نصف ایتم ها را بررسی می کنیم.

🔸 این حالت فقط روی اعداد قابل پیاده سازی است.

#Algorithm #Binary_searching
#الگوریتم

👤 black@root

💎 Channel: @DevelopixPython
💠 یادگیری بدون نظارت

🌀 در شرایطی که سازندگان فقط یک سری داده دارند و قصد دارند به نتایجی برسند که بی‌شک مفید باشند، از یادگیری بدون نظارت استفاده می کنند.

🔸 با‌ توجه به وجود اینترنت و دیجیتال شدن اطلاعات کسب‌و‌کارها، سازمان‌های دولتی و محققان بسیار راحت‌‎تر از گذشته به انبوهی از داده ها و بازار ها اطلاعات بیشتری از مشتریان در اختیار دارند، بیولوژیست ها به داده‌های بیشتری درباره DNA دسترسی دارند و مدیران باک‌ها از فعالیت‌های مالی بیشتری در فایل های خود خبر دارند.

🔹 اگر بازاریاب بخواهد وضعیت مشتری خود را بداند یا اگر تحلیل‌گری به دنبال کشف مغایرت در صورت‌های مالی باشد، یادگیری نظارت‌نشده به هوش مصنوعی کمک می‌کند الگو ها و یا تخلفات را بدون داشتن هرگونه اطلاعاتی از خروجی ها پیدا کند.

🔸 در یادگیری نظارت نشده، داده‎های نمونه فقط شامل ورودی هستند. برای مثال خدمات ویدئویی چون Netflix از الگوریتم هایی استفاده می کنند تا مجموعه مشتریانی را پیدا کنند که عادت آن‌ها برای تماشا فیلم مشابه یکدیگر است تا بتوانند خدمات مستمری به مشتریان مزبور ارائه کنند. اما تنظیم دقیق چنین الگوریتمی می‌تواند پیچیده باشد؛ زیرا بیشتر مردمی که سلیقه های گوناگون دارند معمولاً به چندین گروه تقسیم می‌شوند.

🔹 هوش مصنوعی که آموزش دیده تا از طریق یادگیری نظارت نشده کار کند، می‌تواند الگو هایی را تعیین کند که ممکن است به دلیل پیچیدگی الگو حجم داده ها یا هر دو انسان ها از عهده انجام دادن آن بر نیایند. از آنجا که در آموزش این نوع هوش مصنوعی جزئیات دقیق نتایج مشخص نشده، می‌تواند بینش و شناخت کاملاً نوآورانه ارائه کند. مانند انسانی که آموزش ندیده و خودآموخته است اما هم انسانی که آموزش ندیده و همین نوع هوش مصنوعی می‌توانند نتایج غیر عادی و نامربوط نیز تولید کنند.

🔸 در هردو یادگیری نظارت شده و بدون نظارت هوش مصنوعی برای انجام دادن وظایفی چون کشف روند ها، تعیین تصویر و پیش‌بینی از یک سری داده استفاده می کند. محققان غیر از تحلیل داده ها در پی آموزش هوش مصنوعی بودند که در شرایط و محیط های پویا عمل کنند به این ترتیب سومین گروه مهم یادگیری ماشین یعنی یادگیری تقویتی شکل گرفت.

#Artificial_intelligence #Deep_learning #AI

👤 Dark Dante

💎 Channel: @DevelopixPython
💠 یکی از کاربردهای Bitwise Operators

🌀 اگرچه در زبان‌های سطح پایین‌تر استفاده از Bitwise operatorها مرسوم هستش، در پایتون کمتر نیاز به حل مشکل با استفاده از آن‌ها می‌شود و عموماً از جایگزین‌های highlevel تری استفاده می‌کنیم. ولی دانستن اینکه چه کاری انجام می‌دهند، در برخی موارد می‌تواند مفید باشد.

🔰 فرض کنید می‌خواهیم با یک regex ساده یک عبارت را match کنیم:

import re
print(re.search("ABC.", "abc\n"))

🔸 مچ نشد! حالا کافی است به عنوان flag عدد ۱۸ داده شود:

print(re.search("ABC.", "abc\n", flags=18))

🔹 مچ شد! در واقع با دادن عدد ۱۸ به آن، گفته شد که حساس به حروف بزرگ و کوچک نباشد و کاراکتر‌هایی که مربوط به رفتن به لاین بعد می‌شوند را نقطه در نظر بگیرد. اما چطور با دادن عدد ۱۸ این اتفاق افتاد؟

🔸 موضوع اینجاست که یک سری flag از پیش تعریف شده داریم که توان‌های عدد ۲ هستند:

TEMPLATE = 1 -> 00000001
IGNORECASE = 2 -> 00000010
LOCALE = 4 -> 00000100
MULTILINE = 8 -> 00001000
DOTALL = 16 -> 00010000

🔹 ما درواقع بین IGNORECASE و DOTALL عملگر OR را گذاشتیم که شد: 00010010 (یا همان ۱۸)

🔸 حالا اگر کسی عدد ۱۸ را به ما بدهد، چگونه می‌توان فهمید کدام فلگ‌ها استفاده شدند؟ از عملگر AND استفاده می‌کنیم:
TEMPLATE = 1
IGNORECASE = 2
LOCALE = 4
MULTILINE = 8
DOTALL = 16

flag = 18

if flag & TEMPLATE:
print("Has TEMPLATE flag")

if flag & IGNORECASE:
print("Has IGNORECASE flag")

if flag & DOTALL:
print("Has DOTALL flag")

🔹 به جای پاس دادن عدد ۱۸، می‌توانیم به راحتی از عملگر "|" استفاده کنیم و هیچ مقداری را حفظ نکنیم:

print(re.search("ABC.", "abc\n", flags=IGNORECASE | DOTALL))

🔸 نکته‌ی مفید این بود که با استفاده از یک عدد، توانستیم این اطلاعات را منتقل کنیم.


🔰 در مثال دومی که از ماژول selectors استفاده می‌کنیم، وقتی یک سوکت آماده می‌شود و می‌خواهد به ما بگوید که آماده برای خواندن یا نوشتن است، یک ماسک به ما می‌دهد که براساس آن باید فهمید کدام‌یک انجام شود. به عبارت دیگر، با چنین چیزی روبه‌رو هستیم:

EVENT_READ = (1 << 0)
EVENT_WRITE = (1 << 1)

🔸 اگر مقدار ماسک ۳ باشد، به معنی آن است که سوکت آماده برای خواندن و نوشتن است (همزمان قابلیت خواندن و نوشتن دارد). اگر مقدار ماسک ۲ باشد، سوکت فقط برای نوشتن آماده است و اگر ۱ باشد، سوکت فقط برای خواندن آماده است.

🔹 اگر از همان شرط if بالا استفاده کنیم، با توجه به مقدار عدد دریافتی، می‌توانیم کار مورد نظر را انجام دهیم.

👤 SorousH

💎 Channel: @DevelopixPython
📌 Pass By Reference & Value

🔻 ارجاع بر اساس مقدار (pass by value) و ارجاع بر اساس مرجع (pass by reference) دو مفهومی است که معمولا در مباحث تابع مطرح می شود. اما معمولا در دوره های پایتون درباره‌ی این دو مفهموم صحبت نمیشه ولی بهتره که با این دو مفهموم آشنا باشیم.

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

🔺 در زبان پایتون زمانی که یک متغیر تعریف می‌کنیم یک آدرس در حافظه‌ی رم به متغیر اختصاص داده می‌شود و مقدار متغیر در آن آدرس قرار می گیرد که شما می توانید با استفاده از تابع id آدرس متغیر رو پیدا کنید. برای مثال :

Var = 20
print(id(Var)) # Output : 94587526803432

🔸 نکته‌ ای که وجود دارد این است که آدرس متغیر در حافظه رم ثابت نیست و با هر بار اجرای برنامه، مقدار عوض می شود.

🔺 در زبان های سطح پایین مثل C شما می توانید با استفاده از آدرس متغیر مقدار آن را در حافظه تغییر بدید ولی در زبان های سطح بالا مثل پایتون امکان تغییر مقدار متغیر با استفاده از آدرس آن در حافظه رم وجود نداره.
برای مثال : در زبان C++ می توانید با تعریف یک پوینتر (pointer) مقدار آدرس را عوض کنید.

int age = 10; 
int *address = &age;
*address = 15;
cout << age; // برای چاپ مقدار


🔸 در خط اول یک متغیر از نوع اعداد صحیح ایجاد کردیم و مقدار رو برابر با 10 قرار دادیم؛ بعد یک متغیر پوینتر (برای ذخیره آدرس متغییر) ایجاد کردم سپس آدرس (علامت & مانند تابع id عمل می کنه) متغییر age را در address ذخیره کردم و در لاین بعد مقدار متغیر را به طور غیر‌مستقیم تغییر دادم یعنی به جای نوشتن :
age = 15

به طور غیر مستقیم با استفاده از آدرس متغیر در حافظ رم مقدار متغیر را تغییر دادم.

🔻 با گفتن موارد بالا وقتشه بریم سراغ مطلب اصلی. در زبان های سطح پایین به دو صورت می‌توانید به تابع مقدار ارسال کنید:
1️⃣ مقدار متغیر را ارسال کنید (pass by value).
2️⃣ آدرس متغیر را ارسال کنید (pass by reference).

🔸 در حالت اول شما کپی از متغیر را ارسال می کنید، در واقع یک متغیر در حافطه رم ایجاد می شود. از اونجایی که پایتون از این روش برای ارسال متغیر به سمت تابع استفاده نمی‌کنه مجبوریم مثال از C++ بزنیم تا این موضوع رو درک کنید.

int age = 10;
cout << &age; // output 94587526803432

int hello(age){
cout<<&age; // output 94503976803514
}

hello(age);

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

🔸 در حالت دوم شما آدرس متغیر را ارسال می کنید که تغییرات اعمال شده مستقیما روی متغیر اصلی صورت می‌گیرد.

🔻 روشی که پایتون برای ارسال مقدار به سمت تابع استفاده می‌کند به صورت pass by reference است.
برای مثال :
li = ['python', 'php', 'java']

def change(li:list):
li[0] = "None"

change(li)
print(li) # ['None', 'php', 'java']

🔸 از اونجایی که پایتون به صورت pass by reference عمل می‌کند، آدرس لیست به سمت تابع ارسال می‌شود که هر تغییر صورت بگیرد روی متغیر global صورت می‌گیرد.
به این مثال توجه کنید :
age = 10

def change(num):
age = 50

change(age)
print(age) # 10

🔸 در این تیک کد مقدار age داخل تابع تغییر کرده ولی زمانی که مقدار متغیر را پرینت می‌گیریم هیچ تغییری صورت نگرفته 🤔 اما چرا ؟
قبل از پاسخ دادن به این سوال باید یک چیز را بررسی کنیم در مثال اولی که برای حالت دوم مطرح کردیم یک لیست به سمت تابع ارسال می شود که لیست یک دیتاتایپ mutable (تغییر پذیر) و در مثال دوم اعداد صحیح یک دیتا تایپ immutable (تغییر ناپذیر) است.

🔹 پس در پاسخ به این سوال می‌توان گفت مقداری که به سمت توابع ارسال می‌شود، در صورتی که mutable باشد تغییرات روی متغییر ارسالی اعمال می شود و در غیراینصورت تغییر روی متغییر صورت نمی‌گیرد.

#pass_by_reference #pass_by_value

👤 black@root

💎 Channel: @DevelopixPython
💠 معرفی کتاب
💎 Adaptive Machine Learning Algorithms with Python

📚 این کتاب بر روی استفاده از الگوریتم‌های تطبیقی برای حل مسائل داده‌کاوی و یادگیری ماشین با استفاده از حافظه و توان پردازشی کم تمرکز دارد و با مثال‌های گوناگون به شما کمک می‌کند که به حل این‌گونه مسائل تسلط پیدا کنید تا با استفاده از آن‌ها مسائل داده‌کاوی و یادگیری ماشین را حل کنید.

📖 سرفصل‌های کتاب عبارتند از:

1️⃣ مقدمه
2️⃣ نظریه‌های عمومی و نمادگذاری‌ها
3️⃣ ریشه مربع و ریشه معکوس
4️⃣ بردار اصلی اولیه
5️⃣ بردارهای اصلی و فرعی
6️⃣ محاسبه شتاب داده شده بردارهای اصلی
7️⃣ بردارهای اصلی توسعه‌داده شده
8️⃣ کاربردهای واقعی الگوریتم‌های خطی تطبیقی

📥 این کتاب را می‌توانید از پیامی که در پایین این پست قرار دارد، دانلود کنید.

#Artificial_Intelligence #AI #Machine_learning
#داده_کاوی #یادگیری_ماشین #کتاب #معرفی_کتاب

✍🏻 *ژنرال*

💎 Channel: @DevelopixPython
Adaptive Machine Learning Algorithms with Python.pdf
9.4 MB
📥 دانلود کتاب

📚 Adaptive Machine Learning Algorithms with Python: Solve Data Analytics and Machine Learning Problems on Edge Devices

💎 Channel: @DevelopixPython
💠 عملگرها دستوراتی هستند که بر روی داده‌ها اعمال می‌شود و نتیجه جدیدی تولید می‌کند. بسته به نوع عملگر، ممکن است برای انجام یک عملیات ساده مانند جمع یا ضرب دو عدد، یا برای اعمال منطقی به داده‌ها مانند مقایسه دو عدد یا چک کردن وجود یک عنصر در یک لیست، استفاده شود. در کل، عملگرها یکی از اصلی‌ترین اجزای زبان های برنامه‌نویسی هستند و بدون آنها نمی‌توان عملیات‌های مورد نیاز را انجام داد.

🌀 در ادامه به معرفی انوع عملگرها و کاربرد آن‌ها میپردازیم.

#Operators
#عملگر

✍🏻 *ژنرال*

💎 Channel: @DevelopixPython
| کانال توسعه‌دهندگان پایتون |
💠 عملگرها دستوراتی هستند که بر روی داده‌ها اعمال می‌شود و نتیجه جدیدی تولید می‌کند. بسته به نوع عملگر، ممکن است برای انجام یک عملیات ساده مانند جمع یا ضرب دو عدد، یا برای اعمال منطقی به داده‌ها مانند مقایسه دو عدد یا چک کردن وجود یک عنصر در یک لیست، استفاده…
🔘 عملگرهای ریاضی
🔰 این عملگرها برای انجام عملیات ریاضی از جمله جمع، تفریق، ضرب و تقسیم بر روی اعداد استفاده می‌شوند. علاوه بر این، پایتون از عملگرهای دیگری مانند توان، تقسیم صحیح، باقی‌مانده و ... نیز پشتیبانی می‌کند. این عملگرها از اهمیت ویژه‌ای برخوردارند و با ترکیب آنها با هم می‌توان عملیات‌های پیچیده‌تری را انجام داد.

⚙️ مثال:
a = 2 + 3 # a = 5
b = 4 - 1 # b = 3
c = 5 * 2 # c = 10
d = 7 / 2 # d = 3.5
e = 8 % 3 # e = 2
f = 7 // 2 # f = 3
g = 2 ** 3 # g = 8

🔘 عملگرهای مقایسه‌ای
🔰 مقایسه‌گرها به کاربر این امکان را می‌دهند تا دو مقدار را با یکدیگر مقایسه کنند. به این صورت که این عملگرها مقادیر را با‌هم مقایسه کرده و نتیجه آن را به صورت True یا False برمی‌گردانند.

⚙️ مثال:
a = 5 == 5 # a = True
b = 6 != 6 # b = False
c = 8 > 10 # c = False
d = 4 < 6 # d = True
e = 9 >= 10 # e = False
f = 3 <= 3 # f = True

🔘 عملگرهای منطقی
🔰 این نوع از عملگرها که شامل and ، or و not هستند برای ایجاد شرایط منطقی و بررسی آن‌ها در برنامه‌های پایتون استفاده می‌شوند. این عملگرها برای مقایسه و بررسی درستی یا نادرستی بیان‌ها و شرایط استفاده می‌شوند و به دلیل استفاده در منطق ریاضی به آنها عملگرهای منطقی گفته می‌شود و بسیار حائز اهمیت هستند.

⚙️ مثال:
a = True and False # a = False
b = True or False # b = True
c = not True # c = False

🔘 عملگرهای بیتی
🔰 عملگرهای بیتی در پایتون برای کار با بیت‌های دودویی( باینری «0 و 1» ) به کار می‌روند و به کاربران اجازه می‌دهند تا بر روی بیت‌ها عملیات منطقی مانند AND، OR، XOR و NOT را انجام دهند و از طریق کار با داده‌های دودویی سرعت و بهینه بودن کد را افزایش دهند.

⚙️ مثال:
a = 4 & 5 # a = 4
b = 4 | 5 # b = 5
c = 4 ^ 5 # c = 1
d = ~4 # d = -5
e = 4 << 2 # e = 16
f = 4 >> 1 # f = 2

🔘 عملگر های اختصاصی
🔰 این عملگرها در پایتون برای اختصاص دادن مقدار به یک متغیر با استفاده از عملگرهای ریاضی، منطقی و بیتی استفاده می‌شوند. برای مثال، اگر متغیر a را داشته باشیم و بخواهیم مقدار آن را با یک عدد دیگر جمع کنیم و حاصل را به خودش بازگردانیم، می‌توانیم از عملگر += استفاده کنیم.

⚙️ مثال:
a = 1 # a = 1
b := 2 # (b := 2)
c += 3 # c = c + 3
d -= 4 # d = d - 4
e *= 5 # e = e * 5
f /= 6 # f = f / 6
g //= 7 # g = g // 7
h %= 8 # h = h % 8
i **= 9 # i = i ** 9

🔘 عملگر های ویژه
🔰 عملگرهای ویژه در پایتون عملگرهایی هستند که نحوه‌ی عملکرد آن‌ها با سایر عملگرهای ریاضی و منطقی متفاوت است و برای کاربردهای خاصی طراحی شده‌اند. برای مثال، عملگر is برای بررسی همانی دو متغیر و عملگر in برای بررسی وجود یک مقدار در یک لیست یا رشته استفاده می‌شوند.

⚙️ مثال:
a = "hello"
b = "world"

c = a is b # c = False
d = a is not b # d = True

e = "l" in "hello" # e = True
f = "c" not in "world" # f = True


#Operators
#عملگر

✍🏻 *ژنرال*

💎 Channel: @DevelopixPython
💠 متدهای خاص در کلاس ها

🔸 در پایتون، Magic methodها یا همان Dunder methodها متدهای خاصی هستند که با استفاده از نام‌های خاصی که با _‌_ شروع و به‌‌پایان می‌رسند، به کلاس‌ها رفتارهای پیش فرضی می‌دهند. این نوع متدها به شما این امکان را می‌دهند که کلاس‌های خود را بهتر و با عملکرد بالاتر بنویسید.

🔹 داندرمتدها می‌توانند برای پیاده‌سازی اعمال ریاضی، مقایسه، شیوه ترکیب و تعریف عملکردهای دیگر برای کلاس‌ها استفاده شوند. برای مثال با تعریف متد __add__ می‌توانیم عمل جمع را برای دو شیء در کلاس تعریف شده، به صورت خودکار انجام دهیم. همچنین با تعریف متد __str__ می‌توانیم رشته‌ای را که نشان دهنده نحوه نمایش یک شیء است، تعریف کنیم.

🔸 در کل، Magic method ها در پایتون به کاربران اجازه می‌دهند که رفتار و عملکرد کلاس‌های خود را به شکلی سفارشی و مشخصی تعریف کنند و این باعث افزایش انعطاف پذیری و خوانایی کد می‌شود.

🌀 در ادامه، به معرفی بخش به بخش این نوع از متدها خواهیم پردازیم.

#MagicMethod #DunderMethod #Operator
#داندرمتدها #عملگرها


✍🏻 *ژنرال*

💎 Channel: @DevelopixPython
💠 داندرمتد ها
💠 بخش اول
(موارد مرتبط با عملگرهای ریاضی و مقایسه)

🌀 برای درک بهتر موارد مرتبط با این قسمت، پیشنهاد می‌شود پست مربوط به عملگرها را مطالعه کنید.

🔰 از magic methodهای مرتبط با عملگرهای ریاضی در پایتون برای تعریف عملگرهای ریاضی برای کلاس‌های سفارشی استفاده می‌شود.

🔸 به عنوان مثال، شما می‌توانید با تعریف متد __add__ برای یک کلاس، عملگر جمع + را برای اشیاء این کلاس تعریف کنید. به همین ترتیب، با تعریف دیگر متدهای مربوط به سایر عملگرهای ریاضی، می‌توانید رفتار خاصی را برای این عملگرها در کلاس‌های خود تعریف کنید.

🔸 در کل، استفاده از این magic methodها به شما امکان می‌دهد که به کلاس خود عملکردی مشابه کلاس‌های دیگر در پایتون بدهید و با تعریف رفتارهای خاص برای عملگرهای ریاضی، این عملکرد را بهبود بخشید.

🔹 داندرمتدهای مرتبط به این بخش عبارتند از:
1. __add__(self, other) : +
2. __sub__(self, other) : -
3. __truediv__(self, other) : /
4. __mul__(self, other) : *
5. __floordiv__(self, other) : //
6. __mod__(self, other) : %
7. __pow__(self, other) : **

🔘 برای مثال، میدانیم که در یک رشته، عملگر "-" به‌صورت استاندارد پشتیبانی نمی‌شود زیرا تعریف نشده است. با این حال، با ارث‌بری از کلاس str و اضافه کردن این متد به آن می‌توانیم از آن استفاده کنیم. برای ورودی های غیرقابل‌قبول هم ارورهایی درنظر می‌گیریم. یعنی:

class Str(str):
def __sub__(self, __value: str) -> str:
if not isinstance(__value, str):
raise TypeError(f"value must be str, not {type(__value).__name__}")

elif self.endswith(__value):
return self.removesuffix(__value)

else:
raise ValueError(f'"{self}" does not end with "{__value}"')

Str("DevelopixPython") - "Python" # "Developix"


🔰 از داندرمتدهای مرتبط با عملگرهای مقایسه‌ در پایتون برای تعریف رفتار خاصی برای عملگرهای مقایسه‌ای مانند >, <, >=, <=, == و != استفاده می‌شود. با تعریف این متدها، می‌توانید رفتار خاصی را برای مقایسه دو شیء با هم تعریف کنید و در نتیجه از این مقایسه ها به شکلی خاصی استفاده کنید.

🔸 به عنوان مثال، شما می‌توانید با تعریف متد __eq__ دو شیء را برای برابری باهم مقایسه کنید. همچنین، با تعریف دیگر متدها مانند __gt__ و __lt__، می‌توانید دو شیء را برای مقایسه بزرگتری یا کوچکتری با هم مقایسه کنید.

🔸 در کل، استفاده از magic method های مرتبط با عملگرهای مقایسه در پایتون به شما امکان می‌دهد که رفتار خاصی برای مقایسه دو شیء با هم تعریف کنید.

🔹 داندرمتدهای مرتبط به این بخش عبارتند از:
1. __lt__(self, other) : <
2. __le__(self, other) : <=
3. __gt__(self, other) : >
4. __ge__(self, other) : >=
5. __eq__(self, other) : ==
6. __ne__(self, other) : !=

🔘 فرض کنید می‌خواهید دو مقدار را که تنها در نوع داده‌ با هم تفاوت دارند با یکدیگر مقایسه کنید (برای مثال 123 و "123"). در حالت معمول، اگر از عملگر "==" برای مقایسه این دو داده استفاده کنید، مقدار False برگردانده می‌شود. با این حال، می‌توانیم این متد را override کنیم تا اگر ورودی یک عدد صحیح و مقدار آن‌ها برابر بود، اول داده را تبدیل به str کرده و بعد آن را به متد والد بدهد.
یعنی:
class Str(str):
def __eq__(self, __value: object) -> bool:
if isinstance(__value, int):
return super().__eq__(__value.__str__())

return super().__eq__(__value)

Str("123") == 123 # True

#MagicMethod #DunderMethod #Operator
#داندرمتدها #عملگرها

✍🏻 *ژنرال*

💎 Channel: @DevelopixPython
💠 داندرمتد ها
💠 بخش دوم
(موارد مرتبط با عملگرهای تخصیص و بیتی)

🌀 برای درک بهتر موارد مرتبط با این قسمت، پیشنهاد می‌شود پست مربوط به عملگرها را مطالعه کنید.

🔰 از magic methodهای مرتبط با عملگرهای بیتی در پایتون برای تعریف رفتاری خاص برای عملگرهای بیتی مانند &، | و ... استفاده می‌شود.

🔸 با استفاده از این داندرمتدها، می‌توانید عملگرهای مرتبط را به شکلی سفارشی شده در کلاس خود استفاده کنید. این قابلیت به شما کمک می‌کند تا در برنامه‌های پایتونی که با داده‌های بیتی سروکار دارند، عملگرهای بیتی را با رفتار خاصی تعریف کنید.

🔹 داندرمتدهای مرتبط به این بخش عبارتند از:
1. __and__(self, other) : &
2. __or__(self, other) : |
3. __invert__(self) : ~
4. __xor__(self, other) : ^
5. __rshift__(self, other) : >>
6. __lshift__(self, other) : <<

🔘 فرض کنید قصد دارید دو رشته را با یکدیگر مقایسه کنید و تمامی حروف مشترک آنها را به صورت یک رشته خروجی دهید، به طوری که عملکرد آن مشابه عملکرد استاندارد برای اشتراک در set و dict باشد. در این صورت، شما می‌توانید برای عملگر "&"، متد مربوطه را تعریف کنید که با فراخوانی آن، تمامی حروف مشترک بین دو رشته(یا Iterable) ورودی را درقالب str برگردانید. یعنی:

class Str(str):
def __and__(self, __value: object) -> str:
if isinstance(__value, (Iterable, str)):
return "".join(
letter
for letter in self
if letter in __value
)

raise TypeError(
f"value must be Iterable or str, not {type(__value).__name__}")

Str("Developix") & "Dpx" # "Dpx"

🔰 عملگرهای تخصیص مانند +=، -=، *= و ...، برای انجام عملیات‌های ریاضی، بیتی و ... بر روی یک متغیر استفاده می‌شوند.

🔸 این داندرمتدها، هنگامی که از عملگرهای تخصیص استفاده می‌شود، به صورت خودکار فراخوانی می‌شوند و نتیجه عملیات را در همان شیء ذخیره می‌کنند. به عنوان مثال، اگر a یک شیء از یک کلاس باشد، عملگر += با استفاده از متد __iadd__، مقدار a را با یک مقدار دیگر جمع کرده و نتیجه را در همان a ذخیره می‌کند.

🔹 داندرمتدهای مرتبط به این بخش عبارتند از:
1. __iadd__(self, other) : +=
2. __isub__(self, other) : -=
3. __itruediv__(self, other) : /=
4. __imul__(self, other) : *=
5. __ifloordiv__(self, other) : //=
6. __imod__(self, other) : %=
7. __ipow__(self, other) : **=
8. __iand__(self, other) : &=
9. __ior__(self, other) : |=
10. __ixor__(self, other) : ^=
11. __irshift__(self, other) : >>=
12. __ilshift__(self, other) : <<=
13. __radd__(self, other) : +=
14. __rmul__(self, other) : *=
15. __rfloordiv__(self, other) : //=
16. __rmod__(self, other) : %=
17. __rpow__(self, other) : **=
18. __rand__(self, other) : &=
19. __ror__(self, other) : |=
20. __rxor__(self, other) : ^=
21. __rrshift__(self, other) : >>=
22. __rlshift__(self, other) : <<=
23. __rsub__(self, other) : -=
24. __rtruediv__(self, other) : /=

🔘 بر اساس مثالی که برای عملگر "-" ارائه دادیم، می‌توانیم کد مشابهی برای عملگر "-=" نوشته و از آن استفاده کنیم. در اینجا، فقط با یک تغییر، در صورتی که ورودی نوع int باشد، ابتدا آن را به str تبدیل می‌کنیم و سپس بررسی می‌کنیم که آیا این رشته با ورودی به پایان می‌رسد یا خیر. یعنی:

class Str(str):
def __isub__(self, __value: object) -> bool:
if isinstance(__value, str):
return self.__sub__(__value)

if isinstance(__value, int):
__value = __value.__str__()

elif not isinstance(__value, str):
raise TypeError(f"value must be str, not {type(__value).__name__}")

if self.endswith(__value):
return self.removesuffix(__value)

raise ValueError(f'"{self}" does not end with "{__value}"')

var = Str("123")
var -= 3 # var = "12"

#MagicMethod #DunderMethod #Operator
#داندرمتدها #عملگرها

✍🏻 *ژنرال*

💎 Channel: @DevelopixPython
درود.
تست نویسی یکی از مهم ترین ارکان توسعه نرم افزار هست. چهار تا اصطلاح معروف که موقع تست نویسی به کار میره:

1. False-Negative
کد شما مشکل "داره" + تست داره به غلط میگه که کد شما مشکلی نداره.

2. False-Positive
کد شما مشکلی "نداره" + تست داره به غلط میگه که کد شما مشکل داره.

3. True-Negative
کد مشکلی "نداره" + تست داره به درستی نشون میده که مشکلی نداره.

4. True-Positive
کد مشکل "داره" + تست به درستی نشون میده که مشکل داره .

خیلی از افراد تعاریف دیگری از این ها دارن که کاملا برعکس چیزیه که خوندین. ولی این تعاریف منبعش از کتاب xUnit Test Patterns از آقای Gerard Meszaros که آقای Martin Fowler هم تاییدش کردن.

راه درست فکر کردن بهش هم این هست که به جای "مشکل" یا "باگ" بیاید از "کرونا" استفاده کنید. وقتی تست کرونا میدید و کرونا دارید(باگ دارید) توی تست میزنه POSITIVE. ولی اگه کرونا داشتید و تو تست زد NEGATIVE یعنی یک NEGATIVE عه غلط هست یا همون false negative.

👤 SorousH

💎 Channel: @DevelopixPython
💠 داندرمتد ها
💠 بخش سوم
(Descriptor)

🔰 به طور کلی Descriptorها به شما اجازه می‌دهند تا رفتارهای خاصی را برای متغیرهای یک آبجکت تعریف کنید. این رفتارها ممکن است شامل تعیین، بازیابی، تغییر و حذف مقادیر متغیرهای آن باشند.

🔸 برای تعریف این رفتارها، شما به magic methodهایی نیاز دارید که برای هر نوع descriptor می‌تواند متفاوت باشند. به عنوان مثال، اگر شما به دنبال ایجاد یک descriptor با قابلیت خواندن و نوشتن هستید، باید از __get__ و __set__ استفاده کنید.

🔹 داندرمتدهای مرتبط به این بخش عبارتند از:
1. __get__(self, instance, owner)
2. __set__(self, instance, value)
3. __delete__(self, instance)

🔘 دو کلاس به نام های Python و Developix تعریف می‌کنیم. در کلاس Python، اتریبیوتی به نام group_name تعریف شده است که مقدار اولیه آن برابر با نام کلاس است. وقتی از داندرمتد __get__ استفاده می‌شود، با استفاده از group_name، لینک گروه برگردانده می‌شود و در داندرمتد __set__ با استفاده از value، مقدار group_name تغییر می‌کند.

class Python:
def __init__(self):
self.group_name = self.__class__.__name__

def __get__(self, object_instance, owner_class=None):
return object_instance.gp_link_form.format(self.group_name)

def __set__(self, object_instance, value):
self.group_name = value

class Developix:
Python = Python()

def __init__(self):
self.gp_link_form = "https://t.me/IR{0}GP"

Developix = Developix()
print(Developix.Python) # https://t.me/IRPythonGP

Developix.Python = "PythonNewName"
print(Developix.Python) # https://t.me/IRPythonNewNameGP

#MagicMethod #DunderMethod #Operator
#داندرمتدها #عملگرها

✍🏻 *ژنرال*

💎 Channel: @DevelopixPython
💠 اصل جانشینی لیسکف
💠 Liskov Substitution Principle (LSP)

خانم Barbara Liskov در سال ۱۹۸۷ یه اصلی رو به نام LSP معرفی کردن که یکی از اصول طراحی کلاس ها در Object Oriented Programming هست.

به طور خلاصه این اصل میگه اگر تایپ B از تایپ A ارث بری میکنه، هرجایی که ما نمونه‌ای از تایپ A داشته باشیم باید بتونیم به‌جاش نمونه‌ای از تایپ B رو قرار بدیم بدون اینکه برنامه به مشکل بخوره. در غیر این‌صورت این رابطه ارث بری صحیح نیست.

مثال: فرض کنید یه کلاس base داریم به اسم Bird که یک متد داره به اسم Fly:

class Bird:
def fly(self):
print("I can fly...")

و دو تا subclass داریم به اسم Duck (اردک) و Ostrich (شترمرغ) که از Bird ارث بری میکنن:

class Duck(Bird):
pass

class Ostrich(Bird):
pass

الان اردک میتونه پرواز کنه چون یه پرنده هست، ولی آیا شترمرغ میتونه پرواز کنه؟ نه نمیتونه. آیا پرنده نیست؟ چرا هست. پس این رابطه درست نیست و اصل LSP رو رعایت نمیکنه.

رابطه ی صحیح چطور میتونه باشه؟ اینکه تشخیص بدیم اصلا Fly متد درستی برای Bird نبوده و اون رو به این شکل بازنویسی کنیم:

class Bird:
pass

class FlyingBirds(Bird):
def fly(self):
print("I can fly...")

class Duck(FlyingBirds):
pass

class Ostrich(Bird):
pass

👤 SorousH

💎 Channel: @DevelopixPython
#سوال
خروجی کد زیر چیست ⁉️

✍️ *ژنرال*

💎 Channel: @DevelopixPython
Please open Telegram to view this post
VIEW IN TELEGRAM
#سوال
خروجی کد بالا چیست ⁉️

✍️ *ژنرال*

💎 Channel: @DevelopixPython
Please open Telegram to view this post
VIEW IN TELEGRAM
💠 ماژول OS

🔺 این ماژول کاربردی برای تعامل با سیستم‌عامل به طور پیش‌فرض روی نسخه های پایتونی وجود داره. در ادامه متدهای کاربردی اون رو بررسی می کنیم.

🔻 os.name -> str
🔘 تشخیص نوع سیستم عامل:
os.name # "nt" # windows
os.name # "posix" # linux or MacOS


🔻 os.getcwd() -> str
🔘 دریافت مسیر فعلی.


🔻os.rename(path, newPath)
🔘 تغییر اسم فایل و دایرکتوری:
os.rename("Path/OldName.md", "Path/NewName.md")
os.rename("Path", "NewPath")


🔻 os.access(path, mode) -> bool
🔘 بررسی سطح دسترسی به یک فایل:
Modes :
— F_OK # find
— R_OK # readable
— W_OK # writable
— X_OK # executable

os.access("idk/os.py", os.R_OK) # True



🔻os.listdir(path) -> list
🔘 دریافت لیست فایل و دایرکتوری ها در مسیر مشخص شده.
🔘 در صورتی که مقداری قرار ندید مسیر فعلی رو در نظر می گیره.
os.listdir(
) # ["file_x", "dir_y"]


🔻os.mkdir(path)
🔘 ایجاد دایرکتوری:
os.mkdir("DPX")
os.listdir() #
["file_x", "dir_y", "DPX"]



🔻os.chdir(path)
🔘 تغییر مسیر فعلی:
os.getcwd() # "Path"

os.chdir("NewPath")
os.getcwd() # "NewPath"



🔻os.environ -> dict
🔘 دریافت متغییر های محیطی سیستم عامل.



🔻os.getlogin() -> str
🔘 دریافت نام کاربری که به سیستم لاگین کرده.
os.getlogin() # "coder"



🔻os.walk(path) => generator
🔘 دریافت محتوای یک دایرکتوری و ساب دایرکتوری ها.
os.walk("Path") # [ (address, [folders], [files]) ]


🔻os.makedirs(path)
این متد مثل متد mkdir برای ایجاد فایل مورد استفاده قرار می گیره. ولی تفاوتی که با متد قبلی داره اینه که در متد قبلی وقتی داخل یک دایرکتوری یک دایرکتوری ایجاد می کنیم، باید دایرکتوری اولی وجود داشته باشه تا دایرکتوری دوم ایجاد بشه ولی تو makedirs این مشکل وجود نداره.
os.listdir() # ["file_x", "dir_y"]

os.mkdir("D1/D2/D3")
# Error : FileNotFoundError

os.makedirs("D1/D2/D3")



🔻os.remove(path)
🔘 حذف فایل.

🔻os.rmdir(path)
🔘 حذف دایرکتوری هایی که محتوایی در آن قرار ندارد.

🔻os.system(command)
🔘 اجرا دستورات سیستمی.


💠 منبع :
◽️ https://docs.python.org/3/library/os.html

👤 ȺʍìɾⱮօհąʍʍąժ

💎 Channel: @DevelopixPython
💠 ماژول Shutil

🔺 این ماژول برای کار با فایل مورد استفاده قرار می گیره. در ادامه به بررسی متدهای این ماژول می پردازیم.

🔻shutil.copyfile(path, newPath)
🔘 برای کپی فایل
shutil.copyfile("File.txt", "Dir/NewFile.txt")


🔻 shutil.copy(path, newPath)
🔘 این متد عملکرد متد copyfile داره ولی امکان تغییر اسم فایل وجود نداره و فقط کافیه اسم دایرکتوری مورد نظر بنویسید.
shutil.copy("File.txt", "Dir") # copy file "Dir\File.txt"



🔻shutil.copy2(path, newPath)
🔘 این متد دقیقا عملکرد متد copy داره ولی تفاوتی که وجود داره اینه که متادیتا های فایل رو هم انتقال میده.



🔻shutil.copytree(path, newPath)
🔘 کپی تمام محتوای یک دایرکتوری، نکته‌ایی که وجود داره اینه که باید مقداری که به جای newPath قرار میدید آدرس یک دایرکتوری که از قبل وجود نداره باشه.


🔻shutil.rmtree(path)
🔘 برای حذف دایرکتوری به همراه تمام محتوای موجود در اون استفاده میشه.


🔻shutil.move(path, newPath)
🔘 انتقال فایل.

💠 منبع :
◽️ https://docs.python.org/3/library/shutil.html

👤 ȺʍìɾⱮօհąʍʍąժ

💎 Channel: @DevelopixPython
💢 معرفی میکرو فریمورک‌ فلسک

یکی از دلایل محبوبیت زبان پایتون، داشتن ابزار و فریمورک‌های متعدد و قدرتمند است که کار و عملکرد این زبان را چند برابر و کار توسعه‌دهنده را آسان و سریع‌تر می‌کنند.

یکی از بهترین‌ میکرو فریمورک‌های پایتون برای کار در حوزه وب میکرو فریمورک Flask است که توسط Armin Ronacher با زبان Python طراحی و نوشته شده است و از سال 2010 تحت لایسنس BSD در انتشار عموم قرار گرفته است.

از آنجایی به فلسک یک میکرو فریمورک گفته می‌شود که ساده است و بسیاری از ابزار‌ها و ویژگی‌های رایج فریمورک‌ها را به طور پیش‌فرض ندارد برای مثال فلسک هیچ لایه انتزاعی پایگاه داده و همچنین ابزار و ویژگی‌ای برای اعتبارسنجی فرم‌ها به طور پیش‌فرض ندارد همین امر باعث سادگی آن شده است و برنامه‌نویس درگیر مسائل پیچیده و اضافه نمی‌شود.

با وجود سادگی، Flask قدرت زیادی دارد و شرکت‌های بزرگی مانند LinkedIn، Netflix و Reddit از آن استفاده کرده‌اند.

ویژگی‌های Flask به طور خلاصه عبارتند از:

- استفاده از الگوی Jinja
- مستندات ساده و جامع
- سازگاری با WSGI
- امکان ساخت کوکی‌هایی با امنیت بالا

با استفاده از این ابزارها، توسعه دهندگان قادر خواهند بود فرآیند توسعه برنامه را بهبود بخشیده و با سرعت و سهولت بیشتری پیش بروند.

👤 Maryam

💎 Channel: @DevelopixPython
HashMaps به بیان ساده:
قرار هست ببینیم associative array چیه، hashmap چیه، چه ارتباطی به dictionary داره، ویژگی هاشون چیه، hash collision چیه، چطور برطرف میشن، نمونه خیلی سادش رو پیاده سازی کنیم و در انتها یه نمونه کامل هم ببینیم ازش.

خب... زمانی که ما یک سری دیتا داریم که به هم مرتبط هستن میتونیم اون ها رو توی یه collection نگهداری کنیم مثلا array. اطلاعاتی مثل تمام نمرات دانش آموزان یک کلاس:
grades = [17, 19, 18, ...]
و با ایندکس های عددی بهشون دسترسی پیدا کنیم:
grades[2]
خیلی هم سریع هستن array ها تو دسترسی چون مستقیم میریم سراغ همون نمره ای که نیاز داریم.
مشکل کجاست؟
مشکل اینه که برای ما سخته حفظ کنیم کدوم ایندکس برای کدوم شخص بوده و ترجیح میدیم که اگه نمره ی شخصی رو میخوایم به جای اینکه یه عدد بی معنی بدیم، اسمش رو بدیم و نمرش رو بگیریم:
grades["ali"]
راه حل چیه؟
اینکه اسمش رو(که بهش میگیم کلید) متصل یا مربوط یا "associate" بکنیم به نمرش.
چطوری؟ مثلا:
grades = [("reza", 17), ("sara", 19), ("ali", 18)]
و بعد هم اینجوری میگیریم:
def get_grade(person_name):
for name, grade in grades:
if name == person_name:
return grade

print(get_grade("ali"))
خیلی بهتر و راحت تر شد الان...
چیزی که ما بالا ساختیم یک پیاده سازی (بد) از associative array بود. چون associate کردیم یک کلید رو به مقدارش. associative array یک abstract هست و میتونه به شکل های مختلف پیاده سازی بشه.

خب... فقط یه مشکلی هست الان:
قبلا که با ایندکس میگرفتیم صاف میرفتیم سراغ خودش، الان مجبوریم که iterate کنیم روشون و دونه دونه بگردیم تا برسیم به اونی که میخوایم. کنده!! (شما مثال های این پست رو با ۱۰۰۰ تا داده مثلا تصور کنید)

راه حل چیه؟
اینکه بیایم "یه جوری" این اسم ها رو map کنیم به ایندکس های عددی تا دوباره بتونیم صاف بریم سراغ اونی که میخوایم و سرعت بهتر بگیریم.

چطور اینکارو بکنیم؟
مثلا بیایم یک فانکشن بنویسیم که از کلیدها عدد تولید کنیم به این صورت که اعداد اسکی متناظر با هر حرف از کلیدمون رو باهم جمع کنیم. یعنی برای sara داریم:
s: 115
a: 97
r: 114
در نتیجه:
"sara" # -> 115 + 97 + 114 + 97 -> 423
این میشه فانکشنش:
def hash_func(string):
return sum(map(ord, string))
حالا یه لیست بسازیم که ۵ تا جای خالی داره:
grades = [None, None, None, None, None]

خب حالا الان عددی که از هش کردن(پس یه هش فانکشن ساده بود اون) کلید sara به دست آوردیم و چطور map کنیم به یکی از ایندکس های لیستمون؟ ما که ۴۲۳ تا slot نداریم...
باقی ماندش رو با طول لیستمون حساب کنیم!
اینطوری مطمئن هستیم که داخل اون رنجی که میخوایم هست. پس:
423 % 5 -> 3
کافیه sara و نمرش رو توی ایندکس شماره ۳ ذخیره کنیم:
grades = [None, None, None, ("sara", 19), None]
اگه همین کار رو برای باقی هم بکنیم همچین چیزی میشه:
grades = [
("ali", 18),
None,
None,
("sara", 19),
("reza", 17),
]
(تو پرانتز حواسمون هست که ترتیبش عوض شد...)

الان خوب شد. هر کدوم از نمره هارو بخوایم بگیریم اول اون کلید رو hash میکنیم بعد باقی ماندش رو حساب میکنیم میشه ایندکس مورد نظر. دیگه iteration ای در کار نیست و به time complexity عه O(1) رسیدیم. الان get_grade عه ما اینطوری شد:

def hash_func(string):
return sum(map(ord, string))

def get_grade(person_name):
hash_value = hash_func(person_name)
idx = hash_value % 5
return grades[idx][1]

print(get_grade("reza"))

موقع insert کردن هم دقیقا برعکس همین شکل عمل میکنیم یعنی ابتدا هش میکنیم بعد باقیمانده میگیریم بعد که فهمیدیم کدوم slot برای اون کلید میشه میذاریمش اونجا. درواقع هر چیزی که برای get کردن میگیم برعکسش برای set کردن میشه.

خب ما الان تونستیم associative array رو با کمک hashmap پیاده سازی کنیم :) بریم ادامش...

حالا اگه اسم یکی دیگه از دانشجو ها aras بود چی؟
(پست بعدی)

پست ۱ از ۳

👤 SorousH

💎 Channel: @DevelopixPython
چجوری aras رو اضافه کنیم به grades ؟ چون aras از همون حروفی که sara داره تشکیل شده با هش فانکشنی که ما نوشتیم دوباره بهمون ۴۲۳ میده و اگه باقی مانده بگیریم میشه ۳ یا درواقع همون ایندکسی که برای سارا اختصاص داده شده.
مشکل بوجود اومد... به این مشکل میگن hash collision یا تداخل هش ها!

هش فانکشنی که انتخاب کردیم شاید زیاد جالب نبود چون درواقع به ازای تمام جای گشت های یک کلمه همون هش رو بهمون میده.

هش فانکشن خوب توی hashmap ها دو تا ویژگی داره:
۱- باید محاسباتش سبک باشه. چون دائما داره برای همه ی کلید ها حساب میشه.
۲- "سعی کنه" مقدار های یونیک تولید کنه تا به hash collision بر نخوریم.

بیایم کمی تغییرش بدیم: علاوه بر اینکه از عدد اسکیشون استفاده میکنیم، بیایم اون عدد رو در جایگاهی که داره(حرف چندمه) ضرب هم بکنیم به این شکل:
def hash_func(string):
hash_value = 0
for i, char in enumerate(string, start=1):
hash_value += ord(char) * i
return hash_value

الان هش collision رو بر طرف کردیم:
for name in ("ali", "sara", "reza", "aras"):
hash_value = hash_func(name)
print(f"{name}: {hash_value} : {hash_value % 5}")
خروجیش میشه:
ali:  628  : 3
sara: 1039 : 4
reza: 1070 : 0
aras: 1076 : 1
ولی همونطور که حدس میزنید باز هم با کلید های مختلف ما به hash collision بر میخوریم... مثلا جای aras بذارید nima ...
چه کنیم؟ بیایم یه هش فانکشن معقول داشته باشیم که سعی کنه با سرعت بالا hash value رو محاسبه کنه (چیزی که الان داریم) ولی اگه collision پیش اومد رفعش کنیم! چطور؟

روش اول، separate chaining :
تو این روش میگه به جای اینکه ما بیایم slot ها رو خالی بذاریم (None) ، بیایم به جاش از لیست خالی استفاده کنیم! هر موقع hash collision داشتیم میایم اضافش میکنیم به لیست.

یعنی اگه ۴ تا دانش آموزش ما باشن: ali, sara, reza, nima
با هش فانکشن جدیدی که نوشتیم slot های ما به این صورت میشن:
grades = [
[("reza", 17), ("nima", 20)],
[],
[],
[("ali", 18)],
[("sara", 19)],
]
مشکل حل شد. الان با اینکه وقتی نمره ی نیما رو بخوایم باید قبلش یه رضا رو هم چک کنیم ولی خیلی جلو افتادیم نسبت به اینکه بخوایم همه رو چک کنیم! یعنی کلی کلید رو محاسبه نمیکنیم فقط اون چندتایی که collision داشتن سرچ میشن. (با تعداد بالا تصور کنید)

اگه دقت کنیم میبینیم هرچی hash collision بیشتر داشته باشیم به رفتار خطی بیشتر نزدیک میشیم.
این روش اول بود که پیاده سازی خیلی ساده ای هم داره. یه مشکلی ریزی داریم اینجا. یه سری فضای خالی الان توی slot های ما بوجود اومده. آیا میتونیم بیایم از این فضاها استفاده کنیم؟


روش دوم، open addressing:
شرایطی و در نظر بگیرید که الان reza و ali و sara ذخیره شدن و ما میخوایم nima رو اضافه کنیم:
grades = [
("reza", 17),
None,
None,
("ali", 18),
("sara", 19),
]
میایم nima رو هش میکنیم ایندکس و پیدا میکنیم میبینیم میشه صفر. و نگاه میکنیم میبینیم پر هست! میایم یه sequence ای تولید میکنیم به اسم probing sequence. به طوری که از همون اون ایندکسی که محاسبه کردیم شروع میشه(اینجا شد صفر برای نیما) و یه دور میزنه:
0 -> 1 -> 2 -> 3 -> 4
اگه برای ali میخواستیم probing sequence چی میشد؟
3 -> 4 -> 0 -> 1 -> 2
و به همین ترتیب میریم جلو تا به جای خالی برسیم. الان برای نیما ایندکس بعدی میشه ۱. خالی هست؟ بله. پس میذاریمش اونجا و تبدیل میشه به:
grades = [
("reza", 17),
("nima", 20),
None,
("ali", 18),
("sara", 19),
]
ما ۳ شکل probe sequence داریم:
1- linear probing
2- quadratic probing
3- double hashing

کاری که بالا کردیم linear probing بود. چون نمیخوام بیشتر از این طولانی بشه دوتای دیگه رو اینجا نمیگم(پیاده سازیش رو در انتها گذاشتم) ولی حدس زدنش سادس. مثلا تو دومی به جای اینکه دونه دونه بره بالا ، با توان های ۲ میره بالا (کمک میکنه که توده ای از کلید ها رو یک جای hash table مون نداشته باشیم پخش بشن) و آخری میگه یه هش دیگه(هش دوم) انجام بدیم برای پیدا کردن ایندکس بعدی!

اینا هر کدوم مزایا و معایبی دارن که میشه کلی دربارشون بحث کرد که کدوم کجا چرا بهتره.

پست تموم شد ولی یه سری نکته های تکمیلی باقی موند:
(پست بعدی و آخر)

پست ۲ از ۳

👤 SorousH

💎 Channel: @DevelopixPython