ماجرا بر میگرده به کمکاری من توی نوشتن مطلب برای وبلاگ، من چند وقتی هست که توی یک شرکت به طور ثابت برنامهنویسی میکنم و خب بیشتر وقتم رو اونجا در حال انجام دادن کار های شرکت هستم، برای همین کمتر میرسم برای وبلاگم مطلب بنویسم. یادمه یکی بهم گفته بود جادی با کارفرماش صحبت کرده که بتونه در روز برای تولید محتوا وقت بذاره، برام سوال شد که چطور میشه به کارفرما گفت من میخوام کمتر برات کار کنم و تو شرکت کار های خودم رو هم انجام بدم!
یکی از امکانات جالب پایتون دکوریتورها (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)
برای اولین بار در جنوب کشور (هرمزگان – بندرعباس) قراره ما کرمهای کامپیوتر با همراهی جامعهی کامپیوتر هرمزگان و انجمن علمی کامپیوتر دانشگاه هرمزگان، رویداد لنپارتی رو برگزار کنیم و به این بهونه بازی های آزاد رو بشناسونیم و خوش بگذرونیم
لنپارتی (LAN Party) چی هست؟
لنپارتی یک جور دورهمی و مهمونی اهالی کامپیوتر هست، از زمان های قدیم که بازی های آنلاین نبود یا مثل الان رایج نبود اهالی کامپیوتر دور هم جمع میشدن، سیستم هاشون رو میاوردن و با هم شبکه میکردن و توی شبکه محلی با هم بازی میکردن و خوش میگذروندن.
اهداف اولین لنپارتی ما
جامعهی کامپیوتر هرمزگان بنا به دلایل مختلف متمرکز نیست و از جمله اهداف ما شناخت بیشتر اهالی کامپیوتر در هرمزگان و ارتقای سطح ارتباطات هست. علاوه بر اون آشنایی اهالی کامپیوتر هرمزگان با مبحث بازی های آزاد و در کل نرمافزار های آزاد از دیگر اهداف ما هست، چرا که قرار هست در این لنپارتی بازی آزاد زونوتیک انجام بشه. در نهایت هم که من و فاروق به عنوان بازیکن های قدیمی این بازی به دنبال چند همبازی هستیم! 😄
برگزاری و ثبتنام
برگزاری این رویداد به صورت حضوری و در تاریخ ۱۰ خرداد ۱۴۰۳ انجام میشه، برای حضور هم باید از آدرس زیر بلیط تهیه کنید و هر سیستمی که باهاش میخوایید بازی کنید رو بیارید. (تجهیزات شبکه رو اسپانسر رویداد شرکت هرمزنت تقبل کردند)
یکی از امکانات جالب در پایتون ۳.۷ به بعد اضافه شدن 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 و بلعکس به همراه اعتبارسنجی داده ها به سادگی انجام میشود و نوع داده های مختلفی مثل آدرس ایمیل، عدد مثبت و حتی نوع داده سفارشی شما را پشتیبانی میکند. کلی امکانات دیگر نیز هستند که همهی آن ها را در مستندات کامل این ماژول در سایتشون میتونید بخونید.
سلام به همهٔ خوانندههای خوب وبلاگ کرمهای کامپیوتر. توی این مطلب میخوام بهتون بگم که چرا ما(من، بهنام، و فاروق) توی این موضوع تعداد کمی مطلب داشتیم و به تازگی این دستهبندی رو به کلی حذف کردیم. من و فاروق سالهاست داریم توی دنیای کامپیوتر میچرخیم و تقریباً هر روز ترفندهای جدید رو یاد میگیریم و چهبسا اونها رو امتحان میکنیم! اما دوست نداریم از اونها برای اذیت و آزار مردم استفاده کنیم.
من خودم حتی اقدام به نوشتن ویروس و اجرای اون در محیط ایزوله کردم ولی دلیل اونها فقط جنبهٔ آموزشی برای خودم و امتحان نظریههای مختلف در مورد خرابکاری و نفوذ بوده. اما با همهٔ اینها بازخوردی که از اطرافیان و عامّهٔ مردم میگیریم این نیست که بخوان به هک و نفوذ به جنبهٔ آموزش و چهبسا امنیت خودشون نگاه کنن. متأسفانه اکثر مردم در اینترنت به دنبال یادگیری یک وِرْد جادویی در کامپیوتر و استفادهٔ اون عموماً در جهت اهداف ناپسند هستند. این مسئله باعث میشه که با وجود تأثیری که میتونه در سئوی سایتمون داشته باشه، انگیزهٔ ما رو برای نوشتن در این مورد کم کنه.
الان ممکنه با نوشتن این مطلب یک عده بیان و بخوان که براشون واتساپ و اینستا هک کنیم! این موضوع حتی از مسئلهای که بالا گفتم هم بدتر و کثیفتره! این مسئله رو برای همیشه روشن کنم که امتحان رمزهای مختلف و حمله با فهرست رمزعبور(Password list) و Brute Force هک حساب نمیشه! و دارای ارزش علمی خیلی اندکی هست! پس بدونید که اگر یک موقعی حساب تلگرام یا اینستاتون لو رفت، مشکل از خودتون بود که رمز ناایمن استفاده کردید و درواقع کرک شدید و کلمهٔ هک رو به کار نبرید!
چند هفته پیش تولد ۹ سالگی وبلاگمون بود و توی این مطلب میخوام از تاریخچه ۹ ساله وبلاگمون براتون بگم. من و فاروق از دوران راهنمایی(نسلهای جدید بخوانند متوسطه اول) با هم دوستیم و جفتمون به کامپیوتر و الکترونیک علاقه داشتیم. اولین وبلاگمون رو به اسم pcdanesh روی سرویس بلاگفا ایجاد کردیم و کمی بعد به بلاگ از شرکت بیان منتقلش کردیم.
اون زمان فاروق یک کتابچه از مجموعهی کلید داشت که در مورد وبلاگنویسی گفته بود؛ نوشته بود که در ابتدا شما برای هیچکس مینویسید و کسی مطالبتون رو نمیخونه، نباید این مسئله دلسردتون کنه. کمکم بازدیدکنندهها بیشتر میشن و … به همین خاطر تعداد کم بازدیدها ما رو از نوشتن مطلب منصرف نکرد.
۱۶ دی ماه سال ۹۴ تاریخ اولین مطلبمون روی وبلاگ pcdanesh.blog.ir هست. کمی که مطلب نوشتیم متوجه شدم اسمی که انتخاب کرده بودیم (pcdanesh) به شدت تکراری هست. اینجا بود که تصمیم گرفتیم یه ایده نو بدیم که کسی تا بهحال بهش نرسیده!
من و فاروق کریمیزاده و محمدرضا رضوانی (از دوست های قدیمیمون که الان توی گروهمون خیلی فعاله) با هم داشتیم در مورد اسم وبلاگمون فکر میکردیم. رضوانی میگفت ایدهمون باید خلاقانه باشه، مثل برند butterfly در پینگپونگ که الهام گرفته از راکتها هست. اینجا بود که به شوخی به ذهنم زد پروانه قبل از پروانه شدن کرم هست و ما میتونیم کرم باشیم! در ابتدا به شوخی بود ولی یهو یاد بدافزار هایی موسوم به کرم افتادیم. کرمها بدافزارهایی هستند که در شبکههای محلی تکثیر میشن، در واقع در یک شبکه مثل کافینت از یک سیستم استفاده میکنن تا سیستم های آسیبپذیر رو پیدا و بهش نفوذ کنن.
از اونجایی که من و فاروق در اون زمان زیاد روی برنامهنویسی تمرکز نداشتیم و بیشتر توی کامپیوتر میگشتیم و اکتشاف میکردیم دیدیم جالب میشه که این شوخی رو با خودمون کنیم و بگیم:
ما کرمهای کامپیوتر هستیم و براتون از تجربیات خودمون مینویسیم
و به این ترتیب اسم کرمهای کامپیوتر (pcworms) به وجود اومد. البته pc مخفف personal computer به معنی رایانه شخصی هست، ولی خب اسم کرمهای کامپیوتر شخصی زیاد جالب نیست 😄
اسم و آدرس وبلاگ pcdanesh رو تغییر دادیم. در واقع تاریخ دقیق این جابهجایی مشخص نیست برامون، و یطورایی چون pcdanesh و pcworms از هم جدا نیستن و اولین مطلب هر دو ۱۶ دی ماه هست این تاریخ رو به عنوان تولد وبلاگمون در نظر میگیریم.
ما سال ها روی سرویس blog.ir مطلب نوشتیم و کلی خاطرات شیرین داریم. مثلا یکبار فاروق بعد از نوشتن مطلب توی وبلاگ از سیستم خارج نشد و برادرش که اون موقع سن زیادی نداشت اومد و از روی شیطنت یه مطلب نوشت و پست کرد و توش به یکی از همکلاسی هاش که باهاش زیاد خوب نبود بد و بیراه گفته بود اون موقع ما یه ربات داشتیم که پست های جدید رو از روی RSS میخوند و تو تلگرام میفرستاد. من متوجه شدم که چنین مطلبی منتشر شده و سریع تمام دسترسی های حساب فاروق رو بستم، رمزش رو عوض کردم، مطلب رو پاک کردم و به فاروق زنگ زدم، فاروق که تازه رفته بود بیرون فهمید جریان چیه و برگشت و داداشش رو سرزنش کرد! 😂
سیر تکاملی چت ها هم جالب بود، خیلی وقت پیشها فقط یک چت-روم IRC روی شبکه freenode داشتیم. الان دیگه حتی شبکه freenode هم مثل سابق دیگه نیست! من هم همونطور که گفتم یه بات تو تلگرام داشتم که مطالب رو میفرستاد. اون زمان تلگرام هنوز فیلتر یا سانسور نبود و یه طورایی پیامرسان رسمی بود! بعدها فاروق یک سرور ماتریکس روی Nano Pi خودش اجرا کرد و عضویت در اون با محدودیت بود و برای گرفتن اکانت باید به فاروق ایمیل ارسال میشد. یه طورایی اون گروه فقط برای دنبال کننده های قدیمی وبلاگمون بود که از خیلی وقت پیش با نظراتشون رو وبلاگ بهمون دلگرمی میدادن! بعدا تصمیم گرفتیم که گروه رو بزرگتر کنیم و به سرور های ماتریکس موزیلا رفتیم و علاوه بر اون توی تلگرام هم گروه ساختم و این دو تا رو بهم پل کردیم.
دانشجو شدیم و کلی اتفاقات رو تجربه کردیم! تا سال ۱۴۰۰ که تصمیم گرفتیم از بلاگ جدا بشیم و یک سایت وردپرس داشته باشیم. با بلاگ (بیان) خداحافظی کردیم، بار و بندیل رو جمع کردیم و اومدیم اینجا!
در حال حاضر هم دستاورد جدیدمون دورهمیهای کرمهای کامپیوتر هست که به صورت هفتگی با موضوعات مختلف برگذار میشه.
تجربیاتما در زمینه وبلاگ نویسی
وبلاگنویسی خصوصا برای ما که مطالب تخصصی مینویسیم و افراد خاصی خواننده وبلاگمون هستن همیشه از روی عشق و علاقه بوده. درواقع تا وبلاگ نویس نباشید این رو درک نمیکنید! درواقع ما با هزینه شخصیمون پول دامنه و سرور رو میدیم و برای دل خودمون و کمک به دیگران مطلب مینویسیم. از طرفی نوشتن مطلب برای ما هم تاثیر خوبی داشته، علاوه بر این که افراد بیشتری رو در این زمینه شناختیم و شاید شناخته شدیم! شیوه نوشتن من و فاروق هم تغییر کرده و حتی خودمون بعضی اوقات خواننده وبلاگ خودمون میشیم و چیز هایی که اینجا آموزش دادیم رو دوره میکنیم! ممکنه یک ترفندی رو پیدا کرده باشیم، و توی وبلاگ نوشته باشیم. و بعدش یادمون رفته که ترفنده چی بوده. یک جستجو در وبلاگ انجام میدیم و تمام!
الگوریتم کولهپشتی، یک الگوریتم از نوع حریصانه هست. در این مطلب در مورد الگوریتم کولهپشتی یک و صفر (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) خود سیستم برای ذخیره موقتی اعضا و شمارش آن ها استفاده میکنه. اما باید دقت کنید که این روش نامحدود هم نیست چون به اندازه پشته محدود میشه.
تصمیم گرفتیم که جمعه هر هفته ساعت ۱۸ یک دورهمی دوستانه داشته باشیم. در این دورهمی ها هر هفته یکی از اعضا داوطلب میشه در درمورد موضوعی صحبت کنن و اطلاعاتمون رو رد و بدل کنیم. علاوه بر اون در انتهای هر دورهمی بحث آزاد داریم و کلی میخندیم!
هر هفته اطلاعیه دورهمی رو تو meet.pcworms.ir میزاریم و جمعه ها ساعت ۶ قبل از شروع لینک دورهمی رو توی گروههامون میفرستیم.
تا الان که این مطلب رو مینویسم ۶ دورهمی برگزار شده که به ترتیب موضوعات زیر رو داشتن:
- معرفی دورهمی و این که میخواییم چیکار کنیم
- فاروق کریمیزاده: صفحهنمایش کاغذ الکترونیک
- فاروق کریمیزاده: سختافزارهای متنباز
- بهنام سیمجو: توصیه هایی برای برنامهنویسان تازهوارد
- جعفر فرقانلوژ: فراجستجوگرهای سرکس و موآ + بحث راجع به معادل های فارسی در کامپیوتر!
- پرسش و پاسخ درمورد فراجستجوگرهای سرکس و موآ و بحث گفتوگو راجع به مطلب حریم خصوصی و عصر اطلاعات
من به عنوان یه متخصص کامپیوتر در پاسخ به هشدارم در مورد جمعآوری اطلاعات توسط شرکتهایی مثل گوگل و… همیشه یک پاسخ تکراری میشنوم: «من که هیچ اطلاعات مهم و سرّیای ندارم!»
با توجه به این که امروزه در عصر ارتباطات و اطلاعات هستیم و استفاده از چنین خدماتی بسیار فراگیر شده، لازم میدونم در این مطلب بگم که «چرا اطلاعات شما مهم هستند؟» و «جامعهٔ نرمافزارهای آزاد چه تأثیری بر این مورد دارند؟»
اطلاعات شما مهم نیستند!
اطلاعات خودشون به تنهایی ارزش آنچنانی ندارند اما پردازش آنها بسیار با ارزش خواهند بود. بذارید براتون توضیح بدم. شاید شما یک مامور اطلاعاتی یا یه فرد مهم در یک جایگاه دولتی نباشید اما وقتی اطلاعات شما پردازش شوند، میتواند به افراد دیگر آسیب برساند.
برای مثال خدمات گوگل میتوانند حتی در صورت غیرفعال بودن GPS، موقعیت تقریبی شما را بدانند. این امکان چیزی مخفی و سری نیست، خود گوگل این مورد رو اعلام و در گوشیها امکان استفاده از اون هست! در کل چیز بدی هم نیست، اگه جایی بودید که به ماهوارهٔ GPS دسترسی نبود، باز هم یه موقعیت تقریبی دارید، اما گذشته از این که میتونه برای یهسری افراد بد بشه، جالبه بدونید این امکان با پردازش دادههایی که از افراد عادی به دست اومده، فراهم شده.
چهطوری؟ با استفاده از موقعیت نسبی و دکلهای مخابراتی. هر دکل مخابراتی یک سریال مخصوص داره که گوشی شما هم اون رو دریافت میکنه. امروزه هر گوشی همزمان به بیش از یک دکل وصل هست و اگر در این حین مکانیابی گوشی هم فعال باشه به همراه قدرت سیگنال اتصال یک موقعیت تقریبی از اون دکل داریم. از طرفی با استفاده از یه شبکه (گراف) از اسامی وایفایها و بلوتوثهای نزدیک میشه هم موقعیت نسبی افراد و هم موقعیت وایفای ها رو پیدا کرد.
از طرفی تا به حال متوجه شدید که گوگل میتونه حدس بزنه خونهتون کجاست؟! من متوجه چنین موردی شدم، اگه برید به نقشه google maps احتمالا یه جایی توی نقشه به عنوان خونهٔ شما مشخص شده. حدس من اینه که احتمالاً از مدت زمانی که در یک مکان هستم و هم جاهایی که میرم و برمیگردم به یک نقطهٔ ثابت متوجه این مسئله شده!
حمایت و گسترش
با استفاده از این خدمات، درواقع شما دارید از اون ها حمایت میکنید و بهشون خوراک اطلاعاتی میدید، اونها با پردازش اطلاعاتی که شما بهشون دادید، آمارها و اطلاعات رو استخراج میکنن و اونها رو میفروشن، البته همیشه این اطلاعات چیزهای شخصی نیستند، گاهی سلیقهٔ مصرفکنندههای یه محصول و چیزهای اینچنینی که ارزش تجاری دارند، هستند. اینطوری قدرتشون بیشتر میشه. از طرفی خودبهخود به فراگیر شدن استفادهٔ دیگران از این خدمات کمک میکنید، افراد دیگه که قالباً از چیزهایی که تو این مطلب گفتیم بی اطلاع هستند، مجبور و یا ترغیب به استفاده از چنین خدماتی میشن، در نتیجه کمکم هم قدرت این خدمات و هم مصرفکنندههای اونها بیشتر میشه. مثلا نرمافزار Shareit که همه میشناسیمش، این نرمافزار به تمام پروندهها دسترسی داره و به حریم خصوصی کاربران هم زیاد پایبند نیست، بعد اون وقت هر کسی که میخواد پروندهای برای کسی بفرسته، اولین جملهای که میگه اینه که «shareit نصب کن»!
تأثیرگذاری
از مواردی که گوگل به صورت شفاف گفته که نتایج و تبلیغات رو مطابق سلیقهٔ شما بهتون نشون میده. این هم میتونه مفید و کمککننده باشه و هم میتونه به هدایت شما به سمت یک هدف خاص منجر بشه. نمیخوام از توهم توطئه و کنترل شما توسط این خدمات بگم! ولی فیلتر نتایج یا نمایش هدفمند بعضی از اونها زیاد چیز جالبی نیست! این کار برای این خدمات معمولاً سود تجاری به همراه داره.
یا مثلاً اینستاگرام هم با توجه به علاقهٔ شما، فرستهها رو بهتون نشون میده. حتی بعضی اوقات لابهلای صفحاتی که دنبال میکنید از جاهای دیگه هم فرسته بهتون نشون میده، منجر میشن به این که یه چیزی مثلاً یه مد، یه رفتار یا یه فرهنگ (گاهاً درست و گاهاً غلط) ترند (فراگیر) بشه. یا حتی به یه تحلیل از سلیقهٔ جمعی برسن.
برای مثال در زمان انتخابات در کشورهای بیگانهٔ غربی 😄 با کمک اینفواِنسرها (influencer: افرادی که قدرت تأثیر بر افکار مردم رو دارن، همون شاخهای مجازی!) میتونن تأثیراتی روی دیدگاه و طرز فکر مردم داشته باشن (تعجبی هم نداره؛ تعریف کلمه همینه!) یا مثلاً تأثیری که اونها روی صنعت مد، پوشاک و صنایع آرایشی دارن کاملاً مشخصه.
علاوه بر اون، این روزها الگوریتمها دارن تمام تلاششون رو میکنن که شما رو هر چه بیشتر پای این پلتفرمها نگه دارن که این عوارض بدی براتون به همراه داره.
پ.ن: مواردی که در مورد «تأثیرگذاری» گفته شده تا حدود زیادی مربوط به سواد رسانهای هستن. دانشی که برای هر کسی لازمه که بدونه هر رسانهای چه هدفی داره و برای رسیدن به اون هدفش از چه تکنیکها و ترفندهایی استفاده میکنه
شناخت ارتباطات
بسیاری از شبکههای اجتماعی با تخمین نسبتاً دقیقی میتونن ارتباطات شما با اطرافیانتون رو بسنجن، باز هم شاید این مورد در پیدا کردن دوستهاتون توی شبکههای اجتماعی کمکتون کنه ولی روی تحلیل اطلاعاتی که میخوان از یه شخص خاص جمع کنن تاثیر مستقیم داره.
اعتماد کاذب!
شما (یا خیلیهای دیگه) شاید به نصب یه نرمافزار از یه شرکت ناشناس مثلاً روسی یا چینی مخصوصاً متنبسته شک میکنید ولی خیلی ها به شرکتهایی مثل گوگل یا مایکروسافت اعتماد دارن. اما اگر از مرورگر کروم استفاده میکنید و اون رو به عنوان یه مرورگر امن میشناسید باید بگم که گوگل کروم در ابتدا یک نرم افزار متنباز بود تا زمانی که گوگل تصمیم گرفت یهسری اطلاعات نامعلوم (که شاید چیزهایی نباشن که دوست داشته باشیم!) رو اون پشتمشتها بفرسته به سرورهای خودش یا حتی بدون اطلاع شما به بهونهٔ اسکن ویروسها پروندهها رو اسکن کنه؛ با این که کروم اصلاً یه پادویروس نیست! چنین سرویسی رو خودم خیلی وقت پیشها زمانی که کروم داشتم دیدم، به صورت خودکار در پسزمینه اجرا میشد و امکان غیرفعال کردنش هم نبود! برای همین چیزها بود که عدهای از توسعهدهندههای کروم گفتند که ما نمیخواییم دیگه گوگل کروم رو توسعه بدیم و chromium که متنباز هست رو توسعه میدن (اگر میخوایید از کرومیوم استفاده کنید بگم که من این مرورگر رو پیشنهاد یا تبلیغ نکردم و پیشنهاد میکنم که صرفاً به من اعتماد نکنید و خودتون کرومیوم رو قبل از استفاده بررسی کنید!)
جامعهٔ نرم افزارهای آزاد
آزاد به معنی متنباز بودن نرمافزارها نمیتونن به تنهایی تضمینی برای حفظ حریم خصوصی شما باشه، ولی میتونه روی این موضوع تأثیرگذار باشه. اولاً خیلی از استفادهکنندهها وقت نمیگذارند کدها رو بررسی کنن! دوماً که بعضی از خدمات مثل تلگرام همونطوری که فاروق جان تو مطلب «آیا تلگرام امن و آزاد است» گفت، متن باز بودن نرمافزار گاهی اوقات تأثیری رو حفظ حریم خصوصیتون نداره.
اما این که ارائهدهندههای اون خدمت سخاوتمندانه منبع رو باز گذاشتند و ترسی از لو رفتن هیچ رازی ندارن چیز خوبیه! از طرفی میتونیم بفهمیم که درآمد این خدمات از کجا تأمین میشه (از فروش اطلاعات! یا دونیت یا…) و هم این که میتونیم روی این نرمافزارها تأثیر بگذاریم و اگر برای حریم خصوصی خودمون و دیگران ارزش قائل هستیم اونها رو ویرایش کنیم.
سخن پایانی
این طومار بلند و بالا حرفها و حقایقی هستند که شاید به واسطهٔ موضوع بحث و در کنار هم قرار گرفتنشون کمی بوی توهم توطئه بگیرن یا حتی با درگیر کردن ذهن شما ناخواسته بهتون انرژی منفی وارد کرده باشم! ولی متأسفانه اینها حقایق تلخی هستند که امکان دارند و هم در حال استفاده هستند. این مطالب برای یک بحث سرپایی با اون دوستی که میگه «اطلاعات من ارزش ندارن» طولانی و با جزئیات هستند ولی لازمه که مردم از اون آگاه باشن.
تعریف کلی کاراکتر یا نویسه در کامپیوتر یعنی کوچکترین واحد نمایش اطلاعات که میتونه یک حرف یا علامت باشه. نویسهها شامل فاصله، عددها و علامتها هم هستند!
نویسه در دنیای دیجیتال
در دنیای دیجیتال برای انتقال اطلاعات چیزی جز خاموش و روشن نداریم! جریانهای الکتریکی که قطع و وصل میشن، نوری که چشمک میزنه یا حتی تغییر جهت قطبهای مغناطیسی توی هاردها دنیای دیجیتالما رو میسازن!
به این خاموشها و روشنها میتونیم غلط و صحیح یا صفر و یک رو نسبت بدیم. این صفرها و یکها به کمک قوانین مبنای عددی در ریاضیات معنا پیدا میکنن. یعنی مثلاً عدد ۲ توی کامپیوتر میشه ۰۰۱۰، عدد ۱۱ توی کامپیوتر میشه ۱۰۱۱ و عدد ۵ میشه ۰۱۰۱. در واقع هر عدد، کد مخصوص به خودش رو داره که قابل محاسبه هست. این محاسبه بین مبنای ده (اعداد خودمون) و مبنای دو (اعداد کامپیوتر) به سادگیِ انجام چند تقسیم هست.
برای انتقال اعداد مشکلی نداریم اما اگر بخوایم نویسهها رو منتقل کنیم مشکل خواهیم داشت. برای این کار مجبوریم جدولی بسازیم و برای هر نویسه یک کد تعیین کنیم.
امروز میخوام براتون از رمزنگاری دوسویه یا دو کلیده بگم، این رمزنگاری دنیای اطلاعات رو زیر و رو کرده! تو جهان امروز هر جا رو که نگاه میکنی ردپای این رمزنگاری هست! HTTPS ،SSH، ارز های دیجیتال، شبکههای مجازی و … همه و همه دارن از این روش برای حفظ امنیت استفاده میکنن.
آنچه خواهید خواند!
- در ابتدا خواهم گفت در صورت نبود رمزنگاری اطلاعات چطور لو میروند
- بعد یه دید کلی از رمزنگاری میدم و میگم چرا نمیشه از روش های رایج استفاده کرد
- سپس روش رمزنگاری دوسویه رو به زبون ساده توضیح میدهم
- در ادامه از چگونگی استفاده آن در وب میگم
- چند خط کد مینویسیم و در پایتون از این روش استفاده میکنیم
در این مطلب میگیم که چطور میشه در پایتون یک پروسه دیگر را اجرا کرد و خروجی استاندارد و ورودی استاندارد رو بگیریم و ازش استفاده بکنیم. خروجی و ورودی استاندارد همون چیزایی هستن که تو محیط متنی چاپ میشن یا کاربر توی ورودی برنامه وارد میکنه. در واقع توی این مطلب یاد میگیرید که چطور میتونید در پایتون با برنامه های کنسولی دیگه تعامل کنید.
پایپ (pipe) چیست؟
به طور پیشفرض سیستمعامل ورودیها رو از موس و کیبورد میگیره و خروجیها رو روی صفحهنمایش مینویسه. اما در بعضی مواقع نیاز هست که یک برنامه از خروجیهای یک برنامه (یا دستور) دیگه استفاده کنه یا به ورودی استاندارد یک برنامه داده ارسال کنه. در چنین شرایطی pipe استفاده میشه. pipe یک فضای موقتی در حافظه برای جابهجایی اطلاعات بین دو برنامه هست که البته یک طرفه هم هست؛ یعنی مثلا برای گرفتن خروجی باید از یک pipe و برای نوشتن ورودی هم از یک pipe دیگر باید استفاده کرد.
خیلی ها میگن پایتون زبان سادهای هست، میشه اون رو زود یاد گرفت، بخاطر تعیین نوع پویا(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);