یکی از امکانات جالب پایتون دکوریتورها (Decorators) هست؛ که به طور ساده و خلاصه یک تابع هست که در ورودی یک تابع دیگر را گرفته، روی آن اعمالی انجام میدهد و مقداری که باز میگرداند با نام همان تابع ذخیره میشود. اگر با FastAPI کار کرده باشید حتما با این کد آشنایید:
from fastapi import FastAPI,
app = FastAPI()
@app.get("/")
def hello_world():
return {"Hello": "World"}
یا برای فلسک (Flask) داریم:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
تو این مطلب میخوام بگم جریان این علامت @
چی هست و چه کارایی میتونید باهاش بکنید.
یک دکوریتور چطور کار میکنه
همونطور که قبلا گفتم دکوریتور یک تابع هست که یک پارامتر میگرد و یک مقدار بر میگرداند. خب، با این تعریف که هرچی تابع تک پارامتری داریم دکوریتور میشه! درسته، درواقع چیزی که تفاوت ایجاد میکنه نوع عملکرد علامت @ هست.
نمونه برنامه اول: ایجاد رابط کاربری متنی
فرض کنید برای یک رابط کاربری متنی (CLI) در برنامهتان میخواهید یک دیکشنری (Dictionary) با کلید نام تابع و مقدار تابع کنترل کننده (Handler) داشته باشید. تا بتوانید از آن به این شکل استفاده کنید:
all_commands = {}
...
while True:
line = input("> ")
cmd, *args = line.split()
if cmd not in all_commands:
print("Invalid Command")
continue
all_commands[cmd](*args) # Call the function and send input words (except first) as argument
حالا کد بالا یک خط از کاربر میگیره و با توجه به اولین کلمه در اون خط اگر در کلیدهای دیکشنری بود تابعی که بهش داده بودیم رو روی مابقی کلمات اون خط صدا میزنه مثلا میتونیم توی دیکشنری این رو داشته باشیم:
all_commands["echo"] = print
اینطوری اگه یک نفر بنویسه echo hello world
برنامه بهش hello world
رو نمایش میده. حالا برای توابع بزرگتر باید چیکار کنیم؟ فرض کنید میخواهیم یک دستور (تابع) به نام calc اضافه کنیم تا بتونه محاسبههای ساده رو انجام بده. این تابع به شکل زیر پیادهسازی میشه:
all_commands = {}
def calc(l=None, op=None, r=None):
try:
l, r = int(l), int(r)
except:
print("SyntaxError\n\n usage: calc 2 + 5\nspaces are important")
return
operators = {
"+": lambda: l + r,
"-": lambda: l - r,
"*": lambda: l * r,
"/": lambda: l / r,
"//": lambda: l // r,
}
if op not in operators:
print("operator '%s' is not supported" % op)
return
print(operators[op]())
while True:
line = input("> ")
cmd, *args = line.split()
اگه یک دکوریتور مناسب داشتیم میتونستیم مثل کد پایین خیلی خوشگل و شیک این تابع رو هم به دستورات برنامهمون اضافه کنیم:
@command
def calc(l=None, op=None, r=None):
...
این همه مقدمه چیدم که اینجا بگم چطور بیاییم و برای این نمونه برنامه دکوریتوری به اسم command
بسازیم که هر تابعی که خواستیم به دستورات اضافه کنیم قبلش اون رو بنویسیم و کار رو انجام بده:
all_commands = {}
def command(func: callable) -> callable:
all_commands[func.__name__] = func
return func
@command
def calc(l=None, op=None, r=None):
...
به همین سادگی! گفتم که دکوریتور یه تابعه! تابع command
که به همراه راهنمای نوع ورودی و خروجیهاش (type hint) نوشتمش، میاد و یک شی صدازدنی (یطورایی همون تابع خودمون!) رو میگیره و همون رو بر میگردونه. تو پایتون مثلا زمانی که این دکوریتور رو واسه calc
استفاده میکنیم تابع رو در پارامتر به command
میده و چیزی که command
بر میگردونه رو به اسم calc
ذخیره میکنه. (یعنی اگه command چیزی بر نمیگردوند calc
مقدار None
میگرفت) توی تابع command
اومدیم تابعای رو که توی پارامتر میاد رو توی دیکشنری با اسمش ذخیره کردیم.
کد ما تا اینجای کار این شکلیه:
all_commands = {"echo": print}
def command(func: callable) -> callable:
all_commands[func.__name__] = func
return func
@command
def calc(l=None, op=None, r=None):
try:
l, r = int(l), int(r)
except:
print("SyntaxError\n\n usage: calc 2 + 5\nspaces are important")
return
operators = {
"+": lambda: l + r,
"-": lambda: l - r,
"*": lambda: l * r,
"/": lambda: l / r,
"//": lambda: l // r,
}
if op not in operators:
print("operator '%s' is not supported" % op)
return
print(operators[op]())
while True:
line = input("> ")
cmd, *args = line.split()
if cmd not in all_commands:
print("Invalid Command")
continue
all_commands[cmd](
*args
) # Call the function and send input words (except first) as argument
دکوریتور های پیشرفتهتر!
نمیدونم متوجه فرق دکوریتور ما با اون مثال های اولی که آوردم شدید یا نه، برای دکوریتور هایی که تو مثال های flask و fastapi داشتیم مثلا مینوشتیم @app.get("/")
اما تو دکوریتور خودمون فقط داریم مینویسیم @command
جریان اینه که بعضی مواقع تو دکوریتور نیاز داریم که پارامتر های دیگهای هم بگیریم، مثلا فرض کنید تو برنامه بالایی میخوایی بگیم برای اضافه کردن یه دستور به دیکشنریمون میخواییم یه اسم دلخواه و شاید متفاوت با اسم خود تابع اضافه کنیم. (اول این رو میگم و بعد میرسیم به این که چطور تابعی بسازیم که در جاهای مختلف هم اسم خود تابع رو بذاره و هم اسم دلخواه)
مثلا چنین چیزی میخواییم:
@command("for")
def for_function(start=None, end=None):
try:
start, end = int(start), int(end)
except:
print("SyntaxError\n\n usage: for 2 5\nspaces are important")
for i in range(start, end):
print(i)
مثلا خواستیم دستور for
رو اضافه کنیم ولی چون از کلمه کلیدی های پایتونه نمیتونیم.
برای چنین مواقعی تابعی که برای command
تعریف میکنیم وقتی صدا زده میشه یه تابع دیگه بر میگردونه و پایتون تابع هدف رو به تابع برگردونده شده میده. برای همین تابع command اینطوری میشه:
def command(name: str) -> callable:
def decorator(func:callable):
all_commands[name] = func
return func
return decorator
جریان اینه که خود تابع command
یه رشته (string) در پارامتر به عنوان اسم دستور میگیره و از اونجا که تو پایتون میشه همینطور تابع تو دل تابع نوشت! یه تابع تعریف میکنه که تابعی که میگیره رو به کلید name
تو دیکشنری ذخیره کنه و خود تابع رو مثل قبل برگردونه. تابع command
هم وقتی صدا زده میشه تابعی که تو دل خودشه رو بر میگردونه.
دکوریتور ترکیبی
همونطور که قول دادم! اینجا دکوریتوری مینویسم که هر دو حالت بشه ازش استفاده کرد. البته روشهای مختلفی هست ولی این یک مدلشه:
def command(f: str) -> callable:
def decorator(func: callable):
all_commands[name] = func
return func
if isinstance(f, str):
name = f # `f` is a string so it is the name
return decorator
else:
# `f` is not a string so may be it's a callable
name = f.__name__
# this code may raise AttributeError,
# but this is a simple example, isn't?!
return decorator(f)
تو این کد چک میکنیم ببینیم پارامتری که به command
اومده از چه جنسیه، اگه رشته بود پس اسم دستور هست و اگر نه خود تابع دستور هست. در هر دو صورت اسم دستور با متغیر name
تعیین میشه، وقتی رشته بود name
برابر میشه با رشته داده شده و خود تابع داخلیه فرستاده میشه؛ چون توی کد این تابع به شکل @command("example")
صدا زده شده، پس باید یه تابع تحویل پایتون بده که پایتون تابع هدف رو به اون بفرسته. در غیر این صورت احتمالا پارامتره یه تابعه (چون این دکوریتور رو برای خودمون مینویسیم زیاد پیچیدهش نکردم که همه چیز رو چک کنه!) پس به صورت @command
صدا زده شده و یه تابع بهش داده شده، مثل قبل اسم تابع رو تو name
ذخیره کنه و خودش تابع توی دلش رو روی این تابعی که گرفته صدا بزنه
نمونه برنامه دوم: استفاده از @property
در پایتون
یادش بخیر، اون زمان ها که من C#
کد میزدم یه چیز باحالی داشت به نام property بعدا که اومدم سراغ پایتون دنبالش گشتم و دیدم پایتون هم یه چیزی مشابهاش داره ولی از دکوریتور براش استفاده میکنه. جریان چیه حالا!
این property تو کلاسها هست و یکجور فیلده که موقع گرفتن مقدار یا ذخیره مقدار توش در واقع یه تابع صدا زده میشه. قبلا در مورد dataclass گفتم، اینجا برای این مثال ازش استفاده میکنم تا مجبور نباشم تابع __init__
رو بنویسم، دوست داشتید اون مطلبم رو هم بخونید.
from dataclasses import dataclass
from datetime import date, datetime
@dataclass
class Person:
name: str
birth_date: date
@property
def age(self):
return (date.today() - self.birth_date).days // 365
حالا اگه یه نمونه از این کلاس بسازیم میتونیم فیلد area رو مثل بقیه فیلد ها بگیریم ولی هر بار این فیلد با توجه به دو مقدار ارتفاع و عرض محاسبه میشه:
me = Person("Behnam", date(2000, 9, 29))
print(me.age)
حالا اگه بخواییم این property(خصوصیت) مقدار هم بگیره میتونیم براش یه setter
مشخص کنیم:
from dataclasses import dataclass
from datetime import date, timedelta
@dataclass
class Person:
name: str
birth_date: date
@property
def age(self):
return (date.today() - self.birth_date).days // 365
@age.setter
def age(self):
self.birth_date = date.today() + timedelta(value*365)
یکی از امکانات جالب در پایتون ۳.۷ به بعد اضافه شدن 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 و بلعکس به همراه اعتبارسنجی داده ها به سادگی انجام میشود و نوع داده های مختلفی مثل آدرس ایمیل، عدد مثبت و حتی نوع داده سفارشی شما را پشتیبانی میکند. کلی امکانات دیگر نیز هستند که همهی آن ها را در مستندات کامل این ماژول در سایتشون میتونید بخونید.
الگوریتم کولهپشتی، یک الگوریتم از نوع حریصانه هست. در این مطلب در مورد الگوریتم کولهپشتی یک و صفر (0/1 knapsack algorithm) مینویسم. برای مثال فرض کنید یک کوله پشتی با حجم ۹۰ لیتر دارید؛ و وسایلی که دارید علاوه بر حجم برای شما یک ارزش یا اهمیت دارند و احتمالا همه آنها جا نمیشوند. حال به چه صورت وسایل را انتخاب میکنید تا بیشترین ارزش را در کوله پشتی خود داشته باشید؟ در الگوریتم کولهپشتی یک و صفر یا یک شی انتخاب میشود و یا خیر! امکان برش و تکه کردن نیست.
امروز میخوام براتون از رمزنگاری دوسویه یا دو کلیده بگم، این رمزنگاری دنیای اطلاعات رو زیر و رو کرده! تو جهان امروز هر جا رو که نگاه میکنی ردپای این رمزنگاری هست! HTTPS ،SSH، ارز های دیجیتال، شبکههای مجازی و … همه و همه دارن از این روش برای حفظ امنیت استفاده میکنن.
آنچه خواهید خواند!
- در ابتدا خواهم گفت در صورت نبود رمزنگاری اطلاعات چطور لو میروند
- بعد یه دید کلی از رمزنگاری میدم و میگم چرا نمیشه از روش های رایج استفاده کرد
- سپس روش رمزنگاری دوسویه رو به زبون ساده توضیح میدهم
- در ادامه از چگونگی استفاده آن در وب میگم
- چند خط کد مینویسیم و در پایتون از این روش استفاده میکنیم
در این مطلب میگیم که چطور میشه در پایتون یک پروسه دیگر را اجرا کرد و خروجی استاندارد و ورودی استاندارد رو بگیریم و ازش استفاده بکنیم. خروجی و ورودی استاندارد همون چیزایی هستن که تو محیط متنی چاپ میشن یا کاربر توی ورودی برنامه وارد میکنه. در واقع توی این مطلب یاد میگیرید که چطور میتونید در پایتون با برنامه های کنسولی دیگه تعامل کنید.
پایپ (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);
طی دهههای گذشته بحثهای زیادی راجع به اینکه مشخصات زبانهای برنامهنویسی، به عنوان مثال تعیین نوعشان و مثلا ایستا(static) یا پویا(dynamic) بودن چه تاثیری روی روند توسعه نرمافزارها دارد. یک مقاله علمی(paper) با مطالعه ۶۰۰ پروژه در گیتهاب شامل تقریبا ۷۰ میلیون خط کد و ۳ میلیون کامیت به نتایجی در این مورد رسیدهاست.
حتماً تا به حال با برنامه هایی سروکار داشتید که امکان نصب افزونه داشتند. در این پست قرار هست بهتون بگم چطور میشه توی پایتون برنامهای بنویسیم که بشه بهش افزونه اضافه کرد.
معماری افزونهای (plugin architecture) یک شیوه توسعه نرمافزار است که به برنامهنویس این امکان رو میده که بدون نیاز به ویرایش کد های اصلی برنامهش (هسته/main) قابلیت ها رو با ارائه بستههایی به نام پلاگین در اختیار کاربران قرار بده و کاربران هم میتونن به سادگی با توجه به نیازشون از بین این افزونه ها، اون هایی که میخوان رو به برنامهشون اضافه کنن.
از طرفی میشه از این معماری به عنوان یک پلن تجاری نیز استفاده کرد. مثلا شما میتونید برنامه خودتون رو به صورت رایگان و حتی متنباز عرضه کنید اما با فروش پلاگین ها (و یا ارائه خدمات) کسب درآمدکنید. اگر شما هم یک کاربر وردپرس باشید حتما با این پلن آشنا هستید! 😄
پ.ن: پروژه telegram-post-bot من در گیتهاب از همین ساختار پیروی میکنه و توش امکان اضافه کردن پلاگین هست و درواقع همین پروژه بهانهای شد که من پیادهسازی این معماری رو توی پایتون یاد بگیرم و امتحان کنم.
خب وقتشه که دیگه مقدمه رو بذاریم کنار و بریم سراغ اصل مطلب!
هیچ میدونستین که تو پایتون میتونین برای لیست ها و توپل ها عبارت هایی مثل [1,2,3]>[0,2,3]
نوشت؟ یعنی میتونی خیلی راحت لیست ها رو باهم مقایسه کنی.
اخیرا در یکی از پروژه هایی که داشتم نیاز داشتم تا دو مقدار ورژن رو باهم مقایسه کنم تا مثلا اگر نسخه دوم از اولی جدید تر بود برنامه خودش رو آپدیت کنه، برای اینکار از یک امکان زبان پایتون یعنی مقایسه لیست ها در پایتون استفاده کردم. با من بهنام سیمجو همراه باش تا برات بگم ماجرا چیه!
بنده در دانشگاه هرمزگان دستیار تدریس یا حل التمرین(TA) برنامهسازی پیشرفته هستم بودم. ضمن تصحیح پروژه دانشجویان به اشتباهاتی درست(!) در کد هایشان برخوردم. قسمت درست کدها این میباشد که «کار» میکنند اما به روش درست پایتون نوشته نشدهاند.