جلسه 18 Reflection
مقدمه:
بازتاب (Reflection) به قابلیت یک برنامه در حال اجرا برای بررسی، تجزیه و تحلیل و اصلاح ساختار و رفتار خود در طول زمان اجرا اشاره دارد. در زمینه مهندسی نرمافزار، این مفهوم به ویژه در زبانهای برنامهنویسی شیءگرا کاربرد دارد و امکان دسترسی و تغییر متادیتا (metadata) مانند کلاسها، متدها، ویژگیها و غیره را فراهم میکند. درک بازتاب برای نوشتن کدهای انعطافپذیر، قابل توسعه و قابل نگهداری ضروری است، به خصوص در فریمورکها، ابزارهای تست و سیستمهای پویا.
مثالها:
- دسترسی به ویژگیهای کلاس در زمان اجرا:
فرض کنید کلاسی به نام User دارید:
python class User:
def __init__(self, name, email):
self.name = name
self.email = email
user_instance = User("Alice", "alice@example.com")
# استفاده از بازتاب برای دسترسی به نام ویژگیها و مقادیر آنها
for attr_name in dir(user_instance):
if not attr_name.startswith("__"): # نادیده گرفتن متدهای داخلی
attr_value = getattr(user_instance, attr_name)
print(f"{attr_name}: {attr_value}")
خروجی:
textname: Alice
email: alice@example.com
این مثال نشان میدهد که چگونه میتوانیم در زمان اجرا، نام و مقدار ویژگیهای یک شیء را بدون دانستن قبلی نام آنها، استخراج کنیم.
- فراخوانی متدها در زمان اجرا:
فرض کنید متدهایی در کلاس Calculator دارید:
python class Calculator:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
calc_instance = Calculator()
method_name = "add"
args = (5, 3)
# استفاده از بازتاب برای فراخوانی متد
if hasattr(calc_instance, method_name):
method = getattr(calc_instance, method_name)
result = method(*args)
print(f"Result of {method_name}: {result}")
خروجی:
textResult of add: 8
این مثال چگونگی فراخوانی پویا یک متد بر اساس نام آن در زمان اجرا را نشان میدهد.
- ساخت اشیاء در زمان اجرا:
فرض کنید نام کلاسها را به صورت رشته دریافت میکنید و نیاز دارید اشیاء متناظر را بسازید:
python class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print(f"{self.name} says Woof!")
class Cat:
def __init__(self, name):
self.name = name
def meow(self):
print(f"{self.name} says Meow!")
def create_animal(animal_type, name):
# دریافت کلاس بر اساس نام رشته
AnimalClass = globals()[animal_type]
# ساخت نمونه جدید
return AnimalClass(name)
my_dog = create_animal("Dog", "Buddy")
my_dog.bark()
my_cat = create_animal("Cat", "Whiskers")
my_cat.meow()
خروجی:
textBuddy says Woof!
Whiskers says Meow!
این نمونه نشان میدهد که چگونه میتوان کلاسها را بر اساس نامشان در زمان اجرا پیدا کرد و نمونههایی از آنها ایجاد نمود.
تمرین پایان فصل:
- کلاس
ConfigurationLoader: یک کلاس به نامConfigurationLoaderبنویسید که یک فایل پیکربندی (مانند JSON یا INI) را بخواند. این کلاس باید متدهایی داشته باشد که بتواند مقادیر پیکربندی را بر اساس نام کلیدها در زمان اجرا بازیابی کند. از بازتاب برای ایجاد متدهای getter پویا استفاده کنید؛ یعنی اگر در فایل پیکربندی کلیدdatabase_urlوجود داشت، کلاس باید متدget_database_url()را به صورت پویا ایجاد کند که مقدار مربوطه را برگرداند. - سیستم پلاگین ساده: یک سیستم پلاگین بسیار ساده طراحی کنید. یک کلاس پایه
Pluginتعریف کنید. سپس کلاسهای مختلف پلاگین (مثلاًGreetingPlugin،CalculationPlugin) را بسازید که ازPluginارثبری کنند. یک برنامه اصلی بنویسید که بتواند پلاگینهای موجود در یک پوشه خاص را بارگذاری کند (با استفاده از بازتاب برای یافتن و نمونهسازی کلاسها) و متد اصلی هر پلاگین را فراخوانی کند. - اعتبارسنجی خودکار (Self-Validation): یک کلاس
DataValidatorایجاد کنید. این کلاس باید بتواند ورودیهای خود را بر اساس ویژگیهای (attributes) خاصی که با دکوراتورها (decorators) علامتگذاری شدهاند، اعتبارسنجی کند. به عنوان مثال، یک دکوراتور@requiredبرای فیلدهای اجباری و@numericبرای فیلدهای عددی. از بازتاب برای پیمایش ویژگیهای یک شیء و اعمال قوانین اعتبارسنجی استفاده کنید.
خلاصه نکات کلیدی:
- تعریف: بازتاب قابلیت بررسی، تجزیه و تحلیل و تغییر ساختار و رفتار برنامه در زمان اجرا است.
- کاربردها:
- دسترسی و اصلاح متادیتای کلاسها، متدها و ویژگیها.
- فراخوانی پویا متدها و دسترسی به ویژگیها بر اساس نام.
- ساخت پویا اشیاء و کلاسها.
- توسعه فریمورکها، ابزارهای تست، ORMها (Object-Relational Mappers) و سیستمهای پیکربندی.
- توابع کلیدی (بسته به زبان برنامهنویسی):
getattr(),setattr(),hasattr(),delattr(): برای کار با ویژگیها.dir(): برای لیست کردن ویژگیها و متدها.isinstance(): برای بررسی نوع شیء.type(): برای دریافت نوع (کلاس) یک شیء.globals(),locals(): برای دسترسی به فضاهای نام.exec(),eval(): برای اجرای کد داینامیک (با احتیاط فراوان).