از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
متاکلاس های پایتون و فرابرنامه نویسی
سرفصلهای مطلب
تصور کنید میتوانید برنامههای رایانهای داشته باشید که کد شما را برای شما نوشته باشند. ممکن است، اما ماشین ها نمی نویسند همه کد شما برای شما
این تکنیک به نام فرابرنامه نویسی، در میان توسعه دهندگان چارچوب کد محبوب است. به این ترتیب تولید کد و ویژگیهای هوشمند را در بسیاری از چارچوبها و کتابخانههای محبوب دریافت میکنید Ruby روی ریل یا TensorFlow.
زبان های برنامه نویسی کاربردی مانند Elixir، Clojure و Ruby به دلیل قابلیت های فرابرنامه نویسی خود مورد توجه قرار گرفته اند. در این راهنما، ما به شما نشان می دهیم که چگونه می توانید از قدرت فرابرنامه نویسی در پایتون بهره ببرید. مثالهای کد برای پایتون 3 نوشته شدهاند، اما با برخی تنظیمات برای پایتون 2 کار میکنند.
پایتون یک زبان شی گرا است که کار با کلاس ها را آسان می کند.
متابرنامه نویسی در پایتون متکی است روی نوع جدیدی از کلاس که به نام the متاکلاس. این نوع کلاس، به طور خلاصه، دستورالعملهای مربوط به تولید کد پشت صحنه را دارد که میخواهید هنگام اجرای کد دیگری انجام شود.
ویکیپدیا متاکلاس ها را به خوبی خلاصه می کند:
در برنامه نویسی شی گرا، متاکلاس کلاسی است که نمونه های آن کلاس ها هستند
وقتی یک کلاس را تعریف می کنیم، اشیاء آن کلاس با استفاده از کلاس به عنوان طرح اولیه ایجاد می شوند.
اما خود کلاس چطور؟ نقشه خود کلاس چیست؟
اینجاست که یک متاکلاس وارد می شود. یک متاکلاس طرح اولیه خود کلاس است، درست مانند یک کلاس، طرح اولیه نمونه هایی از آن کلاس است. متاکلاس کلاسی است که ویژگی های کلاس های دیگر را تعریف می کند.
با یک متاکلاس، می توانیم ویژگی هایی را تعریف کنیم که باید به کلاس های جدیدی که در کد ما تعریف شده اند اضافه شوند.
به عنوان مثال، نمونه کد متاکلاس زیر a را اضافه می کند hello
ویژگی هر کلاسی که از این متاکلاس به عنوان الگوی خود استفاده می کند. این بدان معناست که کلاسهای جدیدی که نمونههایی از این متاکلاس هستند دارای a هستند hello
دارایی بدون نیاز به تعریف خود.
class HelloMeta(type):
def hello(cls):
print("greetings from %s, a HelloMeta type class" % (type(cls())))
def __call__(self, *args, **kwargs):
cls = type.__call__(self, *args)
setattr(cls, "hello", self.hello)
return cls
class TryHello(object, metaclass=HelloMeta):
def greet(self):
self.hello()
greeter = TryHello()
greeter.greet()
نتیجه اجرای این کد جدید است TryHello
کلاس قادر است print درودی که می گوید:
greetings from <class '__main__.TryHello'>, a HelloMeta type class
متد مسئول این چاپ در اعلان کلاس اعلام نشده است. بلکه متاکلاس که هست HelloMeta
در این حالت، کدی را در زمان اجرا تولید می کند که به طور خودکار متد را به کلاس اضافه می کند.
برای مشاهده عملی آن، در صورت تمایل کد را در پایتون کپی و جایگذاری کنید console. همچنین برای درک بهتر آنچه در هر قسمت از کد انجام داده ایم، نظرات را بخوانید. ما یک شی جدید به نام داریم greeter
، که نمونه ای از TryHello
کلاس با این حال، ما می توانیم تماس بگیریم TryHello
‘s self.hello
روش حتی اگر چنین روشی در آن تعریف نشده باشد TryHello
اعلامیه کلاس
به جای دریافت خطا برای فراخوانی متدی که وجود ندارد، TryHello
به دلیل استفاده از این روش به طور خودکار به آن اضافه می شود HelloMeta
کلاس به عنوان متاکلاس آن.
متاکلاسها به ما توانایی نوشتن کدی را میدهند که نه فقط دادهها، بلکه کدهای دیگر را تبدیل میکند، مثلاً یک کلاس را در زمانی که نمونهسازی میشود، تبدیل میکند. در مثال بالا، متاکلاس ما یک متد جدید را به طور خودکار به کلاس های جدید اضافه می کند که برای استفاده از متاکلاس خود به عنوان متاکلاس خود تعریف می کنیم.
این نمونه ای از فرابرنامه نویسی است. فرابرنامهنویسی صرفاً نوشتن کدی است که با متاکلاسها و تکنیکهای مرتبط کار میکند تا شکلی از تبدیل کد را در پسزمینه انجام دهد.
نکته زیبای فرابرنامهنویسی این است که به جای خروجی کد منبع، فقط اجرای آن کد را به ما باز میگرداند. کاربر نهایی برنامه ما از اتفاق “جادویی” در پس زمینه بی خبر است.
به چارچوبهای نرمافزاری فکر کنید که در پسزمینه کد تولید میکنند تا مطمئن شوید که شما بهعنوان یک برنامهنویس باید کد کمتری برای همه چیز بنویسید. در اینجا چند نمونه عالی وجود دارد:
خارج از پایتون، کتابخانه های محبوب دیگری مانند Ruby روی ریل (Ruby) و تقویت کنید(C++) نمونههایی از مواردی است که متابرنامهنویسی توسط نویسندگان چارچوب برای تولید کد و مراقبت از چیزهای موجود در پسزمینه استفاده میشود.
نتیجه APIهای کاربر نهایی ساده شده است که کارهای زیادی را برای برنامه نویسی که در چارچوب کدنویسی می کند خودکار می کند.
مراقبت از انجام این سادگی در پشت صحنه، فرابرنامهنویسی زیادی است که در کد منبع فریمورک گنجانده شده است.
برای درک روش کار متاکلاس های پایتون، باید با مفهوم انواع در پایتون خیلی راحت باشید.
یک نوع به سادگی نامگذاری داده یا شی برای یک شی در پایتون است.
پیدا کردن نوع یک شی
با استفاده از Python REPL، بیایید یک شی رشته ساده ایجاد کنیم و نوع آن را به صورت زیر بررسی کنیم:
>>> day = "Sunday"
>>> print("The type of variable day is %s" % (type(day)))
The type of variable day is <type 'str'>
همانطور که انتظار دارید، ما یک نسخه چاپی از متغیر دریافت می کنیم day
از نوع است str
، که از نوع رشته ای است. شما می توانید نوع هر شی را فقط با استفاده از داخلی پیدا کنید type
تابع با یک آرگومان شی.
پیدا کردن نوع کلاس
بنابراین، یک رشته مانند "Sunday"
یا "hello"
از نوع است str
، اما چه؟ str
خودش؟ نوع آن چیست str
کلاس؟
دوباره پایتون را تایپ کنید console:
>>> type(str)
<type 'type'>
این بار، ما یک پرینت دریافت می کنیم که str
از نوع است type
.
نوع و نوع نوع
اما چه در مورد type
خودش؟ چیست type
نوع؟
>>> type(type)
<type 'type'>
نتیجه، یک بار دیگر، “تایپ” است. بنابراین ما آن را پیدا می کنیم type
نه تنها متاکلاس طبقاتی مانند int
، متاکلاس خودش هم هست!
روش های ویژه ای که توسط متاکلاس ها استفاده می شود
در این مرحله ممکن است کمی مرور نظریه کمک کند. به یاد داشته باشید که متاکلاس کلاسی است که نمونه های آن خود کلاس هستند و نه فقط اشیاء ساده.
در پایتون 3 میتوانید با عبور از Masterclass مورد نظر به تعریف کلاس جدید، یک متاکلاس به ایجاد یک کلاس جدید اختصاص دهید.
این type
type به عنوان متاکلاس پیشفرض در پایتون، روشهای خاصی را تعریف میکند که متاکلاسهای جدید میتوانند برای پیادهسازی رفتار تولید کد منحصربهفرد، آنها را لغو کنند. در اینجا مروری کوتاه بر این روش های “جادویی” موجود است روی یک متاکلاس:
__new__
: این روش نامیده می شود روی متاکلاس قبل از یک نمونه از کلاس مبتنی بر روی متاکلاس ایجاد می شود__init__
: این متد برای تنظیم مقادیر پس از ایجاد instance/object فراخوانی می شود__prepare__
: فضای نام کلاس را در یک نقشه برداری که ویژگی ها را ذخیره می کند، تعریف می کند__call__
: این متد زمانی فراخوانی می شود که از سازنده کلاس جدید برای ایجاد یک شی استفاده شود
اینها روشهایی هستند که باید در متاکلاس سفارشی خود را نادیده بگیرید تا رفتار کلاسهای شما متفاوت از رفتار کلاسهای شما باشد type
، که متاکلاس پیش فرض است.
بیایید قبل از استفاده از تمرین فرابرنامهنویسی متاکلاس یک قدم به عقب برگردیم. یکی از کاربردهای رایج فرابرنامه نویسی در پایتون، استفاده از دکوراتورها است.
دکوراتور تابعی است که اجرای یک تابع را تغییر می دهد. به عبارت دیگر، تابعی را به عنوان ورودی می گیرد و تابع دیگری را برمی گرداند.
به عنوان مثال، در اینجا یک دکوراتور وجود دارد که هر عملکردی را انجام می دهد، و نام تابع را قبل از اجرای عملکرد اصلی به طور معمول چاپ می کند. این می تواند برای ثبت تماس های تابع مفید باشد، به عنوان مثال:
from functools import wraps
def notifyfunc(fn):
"""prints out the function name before executing it"""
@wraps(fn)
def composite(*args, **kwargs):
print("Executing '%s'" % fn.__name__)
rt = fn(*args, **kwargs)
return rt
return composite
@notifyfunc
def multiply(a, b):
product = a * b
return product
شما می توانید کد را کپی و در یک Python REPL جایگذاری کنید. نکته تمیز در مورد استفاده از دکوراتور این است که تابع ترکیبی به جای تابع ورودی اجرا می شود. نتیجه کد بالا این است که تابع ضرب قبل از اجرای محاسبات خود را در حال اجرا اعلام می کند:
>>> multiply(5, 6)
Executing 'multiply'
30
>>>
>>> multiply(89, 5)
Executing 'multiply'
445
به طور خلاصه، دکوراتورها به همان رفتار تبدیل کد متاکلاس ها دست می یابند، اما بسیار ساده تر هستند. شما می خواهید از دکوراتورها در جایی که نیاز به اعمال فرابرنامه نویسی رایج در اطراف کد خود دارید استفاده کنید. به عنوان مثال، می توانید یک دکوراتور بنویسید که همه تماس های پایگاه داده را ثبت کند.
متاکلاس ها می توانند ویژگی های کلاس ها را جایگزین یا تغییر دهند. آنها قدرت دارند hook قبل از ایجاد یک شی جدید یا بعد از ایجاد شی جدید. نتیجه انعطاف پذیری بیشتر در مورد آنچه می توانید از آنها استفاده کنید است.
در زیر، ما یک متاکلاس ایجاد می کنیم که به همان نتیجه دکوراتور از مثال قبلی می رسد.
برای مقایسه این دو، باید هر دو مثال را در کنار هم اجرا کنید و سپس کد منبع مشروح شده را دنبال کنید. توجه داشته باشید که اگر REPL شما قالب بندی کد را حفظ می کند، می توانید کد را کپی کرده و مستقیماً در REPL خود جایگذاری کنید.
import types
def notify(fn, *args, **kwargs):
def fncomposite(*args, **kwargs):
print("running %s" % fn.__name__)
rt = fn(*args, **kwargs)
return rt
return fncomposite
class Notifies(type):
def __new__(cls, name, bases, attr):
for name, value in attr.items():
if type(value) is types.FunctionType or type(value) is types.MethodType:
attr(name) = notify(value)
return super(Notifies, cls).__new__(cls, name, bases, attr)
class Math(metaclass=Notifies):
def multiply(a, b):
product = a * b
print(product)
return product
Math.multiply(5, 6)
class Shouter(metaclass=Notifies):
def intro(self):
print("I shout!")
s = Shouter()
s.intro()
کلاس هایی که از ما استفاده می کنند Notifies
به عنوان مثال متاکلاس Shouter
و Math
، در زمان ایجاد، روشهای آنها با نسخههای پیشرفته جایگزین میشوند که ابتدا از طریق a به ما اطلاع میدهند print
بیانیه نام روشی که اکنون در حال اجرا است. این مشابه رفتاری است که ما قبل از استفاده از یک عملکرد تزئینی اجرا کردیم.
موارد استفاده رایج برای فرابرنامهنویسی شامل کنترل نمونههای کلاس است.
مثلا، تک قلوها در بسیاری از کتابخانه های کد استفاده می شود. یک کلاس singleton ایجاد نمونه را کنترل می کند به طوری که حداکثر یک نمونه از کلاس در برنامه وجود دارد.
کلاس نهایی نمونه دیگری از کنترل استفاده از کلاس است. با کلاس نهایی، کلاس اجازه ایجاد زیر کلاسها را نمیدهد. کلاسهای نهایی در برخی از چارچوبها برای امنیت استفاده میشوند و اطمینان میدهند که کلاس ویژگیهای اصلی خود را حفظ میکند.
در زیر، پیادهسازی یک کلاس نهایی را با استفاده از یک متاکلاس ارائه میکنیم تا کلاس را از ارث بردن کلاس توسط دیگری محدود کنیم.
class Final(type):
def __new__(cls, name, bases, attr):
type_arr = (type(x) for x in bases)
for i in type_arr:
if i is Final:
raise RuntimeError("You cannot subclass a Final class")
return super(Final, cls).__new__(cls, name, bases, attr)
class Cop(metaclass=Final):
def exit():
print("Exiting...")
quit()
class FakeCop(Cop):
def scam():
print("This is a hold up!")
cop1 = Cop()
fakecop1 = FakeCop()
class Goat(metaclass=Final):
location = "Goatland"
class BillyGoat(Goat):
location = "Billyland"
در کد، ما اعلانهای کلاس را برای تلاش برای زیر کلاس a قرار دادهایم Final
کلاس این اعلامیه ها شکست می خورند، و در نتیجه استثناهایی ایجاد می شود. استفاده از یک متاکلاس که کلاس های فرعی آن را محدود می کند، ما را قادر می سازد تا کلاس های نهایی را در پایگاه کد خود پیاده سازی کنیم.
پروفایلرها برای بررسی میزان استفاده از منابع در یک سیستم محاسباتی استفاده می شود. یک نمایه ساز می تواند مواردی مانند میزان استفاده از حافظه، سرعت پردازش و سایر معیارهای فنی را ردیابی کند.
ما می توانیم از متاکلاس برای پیگیری زمان اجرای کد استفاده کنیم. مثال کد ما یک نمایه ساز کامل نیست، بلکه اثباتی بر مفهوم این است که چگونه می توانید فرابرنامه نویسی را برای عملکردهای پروفایلر انجام دهید.
import types
import time
class Timer:
def __init__(self, func=time.perf_counter):
self.elapsed = 0.0
self._func = func
self._start = None
def start(self):
if self._start is not None:
raise RuntimeError('Already started')
self._start = self._func()
def stop(self):
if self._start is None:
raise RuntimeError('Not started')
end = self._func()
self.elapsed += end - self._start
self._start = None
def reset(self):
self.elapsed = 0.0
@property
def running(self):
return self._start is not None
def __enter__(self):
self.start()
return self
def __exit__(self, *args):
self.stop()
def timefunc(fn, *args, **kwargs):
def fncomposite(*args, **kwargs):
timer = Timer()
timer.start()
rt = fn(*args, **kwargs)
timer.stop()
print("Executing %s took %s seconds." % (fn.__name__, timer.elapsed))
return rt
return fncomposite
class Timed(type):
def __new__(cls, name, bases, attr):
for name, value in attr.items():
if type(value) is types.FunctionType or type(value) is types.MethodType:
attr(name) = timefunc(value)
return super(Timed, cls).__new__(cls, name, bases, attr)
class Math(metaclass=Timed):
def multiply(a, b):
product = a * b
print(product)
return product
Math.multiply(5, 6)
class Shouter(metaclass=Timed):
def intro(self):
print("I shout!")
s = Shouter()
s.intro()
def divide(a, b):
result = a / b
print(result)
return result
div = timefunc(divide)
div(9, 3)
همانطور که می بینید، ما توانستیم یک را ایجاد کنیم Timed
متاکلاسی که کلاس های خود را بازنویسی می کند روی-پرواز. هر زمان که یک کلاس جدید که از Timed
متاکلاس اعلام شده است، متدهای آن بازنویسی می شوند تا توسط کلاس ابزار تایمر ما زمان بندی شوند. هر زمان که محاسبات را با استفاده از a اجرا می کنیم Timed
کلاس، زمانبندی را بهطور خودکار برای خودمان انجام میدهیم، بدون نیاز به انجام کار اضافی.
اگر در حال نوشتن کدها و ابزارهایی هستید که توسط توسعه دهندگان دیگر مانند فریمورک های وب یا دیباگرها استفاده می شود، متابرنامه یک ابزار عالی است. با تولید کد و فرابرنامهنویسی، میتوانید زندگی را برای برنامهنویسانی که از کتابخانههای کد شما استفاده میکنند، آسان کنید.
متاکلاس ها و فرابرنامه نویسی قدرت زیادی دارند. نکته منفی این است که فرابرنامهنویسی میتواند نسبتاً پیچیده شود. در بسیاری از موارد، استفاده از دکوراتورها راه ساده تری را برای دستیابی به یک راه حل زیبا فراهم می کند. زمانی باید از متاکلاس ها استفاده کرد که شرایط به جای سادگی، عمومیت را طلب می کند.
برای استفاده موثر از متاکلاس ها پیشنهاد می کنیم خواندن در مقام رسمی متاکلاس های پایتون 3 مستندات.
(برچسبها به ترجمه)# python
منتشر شده در 1403-01-28 07:39:04