تم نشر هذه المقالة كجزء من مدونة علوم البيانات
المُقدّمة
تحتوي مكتبة Python القياسية على العديد من الوحدات النمطية الرائعة للمساعدة في الحفاظ على نظافة التعليمات البرمجية الخاصة بك وأبسطها بالتأكيد واحدة من
Caching
لنبدأ ببعض من أبسط الوظائف القوية للوحدات الوظيفية للوحدات. لنبدأ بوظائف التخزين المؤقت (وكذلك المصممين) - lru_cache
,cache
و cached_property
. أولهم - lru_cache
يوفر ذاكرة تخزين مؤقت للنتائج الأخيرة لتنفيذ الوظائف ، أو بعبارة أخرى ، يتذكر نتيجة عملهم:
from functools import lru_cache
import requests @lru_cache(maxsize=32)
def get_with_cache(url): try: r = requests.get(url) return r.text except: return "Not Found" for url in ["https://google.com/", "https://reddit.com/", "https://google.com/", "https://google.com/"]: get_with_cache(url) print(get_with_cache.cache_info())
# CacheInfo(hits=2, misses=4, maxsize=32, currsize=4)
print(get_with_cache.cache_parameters())
# {'maxsize': 32, 'typed': False}
في هذا المثال ، نجري طلبات GET ونخزن نتائجها مؤقتًا (حتى 32 نتيجة) باستخدام أداة تزيين @lru_cache
. لمعرفة ما إذا كان التخزين المؤقت يعمل بالفعل ، يمكنك التحقق من معلومات ذاكرة التخزين المؤقت للوظيفة باستخدام طريقة cache_info
يُظهر عدد مرات الدخول إلى ذاكرة التخزين المؤقت وعدد مرات الوصول إليها. يوفر المصمم أيضًا طرقًا clear_cache
و cache_parameters
لإلغاء النتائج المخبأة ومعلمات الاختبار ، على التوالي.
إذا كنت بحاجة إلى المزيد من التخزين المؤقت الدقيق ، فيمكنك تضمين وسيطة اختيارية مكتوبة = true ، والتي تتيح لك تخزين أنواع مختلفة من الوسائط مؤقتًا بشكل منفصل.
مصمم آخر للتخزين المؤقت في functools هو وظيفة تسمى ببساطة cache
. إنه غلاف بسيط lru_cache
يحذف الوسيطة max_size ، ويقللها ، ولا يزيل القيم القديمة.
ديكور آخر يمكنك استخدامه للتخزين المؤقت هو cached_property
. كما يوحي الاسم ، يتم استخدامه لتخزين نتائج سمات الفئة مؤقتًا. هذا الميكانيكي مفيد للغاية إذا كان لديك خاصية مكلفة للحساب ، لكنها تظل كما هي.
from functools import cached_property class Page: @cached_property def render(self, value): # Do something with supplied value... # Long computation that renders HTML page... return html
يوضح هذا المثال البسيط. كما يمكن استخدامه ، الخاصية المخزنة مؤقتًا ، على سبيل المثال ، لتخزين صفحة HTML معروضة مؤقتًا والتي يجب عرضها على المستخدم مرارًا وتكرارًا. يمكن القيام بالشيء نفسه بالنسبة لبعض استعلامات قاعدة البيانات أو الحسابات الرياضية المطولة.
جمال آخر cached_property
هو أنه يعمل فقط عند البحث ، لذلك يسمح لنا بتغيير قيمة السمة. بعد تغيير السمة ، لن تتغير القيمة المخزنة مؤقتًا مسبقًا ، وبدلاً من ذلك ، سيتم حساب القيمة الجديدة وتخزينها مؤقتًا. يمكنك أيضًا مسح ذاكرة التخزين المؤقت ، وكل ما عليك فعله هو إزالة السمة.
أرغب في إنهاء هذا القسم بتحذير حول جميع أدوات التزيين المذكورة أعلاه - لا تستخدمها إذا كانت وظيفتك لها بعض الآثار الجانبية أو إذا كانت تخلق كائنات قابلة للتغيير في كل مرة يتم استدعاؤها ، حيث من الواضح أن هذه ليست الوظائف التي تريد تخزينها مؤقتًا .
المقارنة والطلب
ربما تعلم بالفعل أنه يمكنك تنفيذ عوامل المقارنة في Python مثل <
, >=
or ==
، مع lt
, gt
or eq
. ومع ذلك ، قد يكون من المحبط للغاية إدراك كل من eq
, lt
, le
, gt
or ge
. لحسن الحظ ، هناك أدوات تزيين هناك @total_ordering
يمكن أن يساعدنا في ذلك ، لأن كل ما نحتاج إلى تنفيذه هو eq
إحدى الطرق المتبقية ، وسيتم إنشاء باقي مصمم الديكور تلقائيًا.
from functools import total_ordering @total_ordering
class Number: def __init__(self, value): self.value = value def __lt__(self, other): return self.value Number(3))
# True
print(Number(1) = Number(15))
# True
print(Number(10) <= Number(2))
# False
بهذه الطريقة ، يمكننا تنفيذ جميع عمليات المقارنة الموسعة ، على الرغم من وجود المعادلة فقط واليدوي lt. الميزة الأكثر وضوحًا هي الراحة ، وهي أنك لست مضطرًا لكتابة كل هذه الأساليب السحرية الإضافية ، ولكن ربما يكون الأمر الأكثر أهمية هو تقليل مقدار الكود وقابلية قراءته بشكل أفضل.
الزائد
ربما تعلمنا جميعًا أنه لا يوجد تحميل زائد في Python ، ولكن هناك بالفعل طريقة سهلة لتنفيذه باستخدام وظيفتين من functools ، وهما الإرسال الفردي و / أو طريقة الإرسال الفردي. تساعدنا هذه الوظائف في تنفيذ ما نسميه خوارزمية إرسال متعددة تسمح للغات البرمجة المكتوبة ديناميكيًا مثل Python بالتمييز بين الأنواع في وقت التشغيل.
جزئي
نعمل جميعًا مع مكتبات أو أطر عمل خارجية مختلفة ، يوفر الكثير منها وظائف وواجهات تتطلب منا تمرير عمليات الاسترجاعات ، مثل العمليات غير المتزامنة أو الاستماع للأحداث. هذا ليس شيئًا جديدًا ، ولكن ماذا لو احتجنا أيضًا إلى تمرير بعض الوسائط مع رد النداء. هذا هو المكان الذي يأتي فيه في Functools سهلة الاستخدام.partial
. يمكن استخدامها partial
لتجميد بعض (أو كل) وسيطات دالة عن طريق إنشاء كائن جديد بتوقيع دالة مبسط. خجول؟ دعنا نلقي نظرة على بعض الأمثلة العملية:
def output_result(result, log=None): if log is not None: log.debug(f"Result is: {result}") def concat(a, b): return a + b import logging
from multiprocessing import Pool
from functools import partial logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("default") p = Pool()
p.apply_async(concat, ("Hello ", "World"), callback=partial(output_result, log=logger))
p.close()
p.join()
يوضح الكود أعلاه كيف يمكنك استخدامه partial
لتمرير وظيفة ( output_result
) مع حجة ( log=logger
) بمثابة رد اتصال. في هذه الحالة ، سنستخدم multrocessing.apply_async ، والتي تحسب بشكل غير متزامن نتيجة الوظيفة ( concat
) وإرجاع نتيجة رد الاتصال. لكن، apply_async
ستمرر دائمًا النتيجة باعتبارها الوسيطة الأولى ، وإذا أردنا تضمين أي وسيطات إضافية ، كما هو الحال مع log = logger ، فنحن بحاجة إلى استخدام جزئي.
لقد درسنا حالة استخدام متقدمة إلى حد ما ، ومثال أبسط هو الإنشاء المعتاد لوظيفة تكتب في stderr بدلاً من stdout:
import sys
from functools import partial print_stderr = partial(print, file=sys.stderr)
print_stderr("This goes to standard error output")
بهذه الحيلة البسيطة ، أنشأنا وظيفة جديدة قابلة للاستدعاء تمر دائمًا file=sys.stderr
كوسيطة مسماة للإخراج ، مما يسمح لنا بتبسيط الكود الخاص بنا دون الحاجة إلى تحديد قيمة الوسيطة المسماة في كل مرة.
ومثال جيد أخير. يمكننا ان نستخدم partial
بالتزامن مع وظيفة غير معروفة iter
لإنشاء مكرر عن طريق تمرير كائن قابل للاستدعاء و sentinel
in iter
، والتي يمكن تطبيقها على النحو التالي:
from functools import partial RECORD_SIZE = 64 # Read binary file...
with open("file.data", "rb") as file: records = iter(partial(file.read, RECORD_SIZE), b'') for r in records: # Do something with the record...
عادة ، عند قراءة ملف ، نريد أن نكرره عبر الأسطر ، ولكن في حالة البيانات الثنائية ، قد نحتاج إلى تكرار السجلات ذات الحجم الثابت. يمكنك القيام بذلك عن طريق إنشاء كائن قابل للاستدعاء باستخدام partial
يقرأ الجزء المحدد من البيانات ويمررها iter
لإنشاء مكرر. يستدعي هذا المكرر بعد ذلك وظيفة القراءة حتى تصل إلى نهاية الملف ، مع أخذ حجم القطعة المحدد دائمًا فقط ( RECORD_SIZE
). أخيرًا ، عند الوصول إلى نهاية الملف ، القيمة sentinel
(ب ") يتم إرجاعه ويتوقف التكرار.
ديكور
لقد تحدثنا بالفعل عن بعض المصممين في الأقسام السابقة ، لكن ليس عن المصممين لخلق المزيد من الزخارف. أحد هؤلاء المصممين هو functools.wraps
. لفهم سبب حاجتك إليها ، دعنا نلقي نظرة على مثال:
def decorator(func): def actual_func(*args, **kwargs): """Inner function within decorator, which does the actual work""" print(f"Before Calling {func.__name__}") func(*args, **kwargs) print(f"After Calling {func.__name__}") return actual_func @decorator
def greet(name): """Says hello to somebody""" print(f"Hello, {name}!") greet("Martin")
# Before Calling greet
# Hello, Martin!
# After Calling greet
يوضح هذا المثال كيف يمكن تنفيذ مصمم بسيط. نلتف وظيفة تؤدي مهمة محددة ( actual_func
) بمصمم خارجي ، ويصبح المصمم نفسه ، والذي يمكن بعد ذلك تطبيقه على وظائف أخرى ، على سبيل المثال ، كما هو الحال مع greet
. عندما تستدعي الوظيفة ، greet
سترى أنه يطبع الرسائل على حد سواء من actual_func
ومن تلقاء نفسها. يبدو بخير ، أليس كذلك؟ لكن ماذا يحدث إذا فعلنا هذا:
print(greet.__name__)
# actual_func
print(greet.__doc__)
# Inner function within decorator, which does the actual work
عندما نسمي اسم وتوثيق الوظيفة المزخرفة ، ندرك أنه قد تم استبدالها بقيم من وظيفة الديكور. هذا أمر سيء لأننا لا نستطيع إعادة كتابة جميع أسماء وظائفنا ووثائقنا عندما نستخدم بعض المصممين. كيف يمكن حل هذه المشكلة؟ بالطبع ، مع functools.wraps
:
from functools import wraps def decorator(func): @wraps(func) def actual_func(*args, **kwargs): """Inner function within decorator, which does the actual work""" print(f"Before Calling {func.__name__}") func(*args, **kwargs) print(f"After Calling {func.__name__}") return actual_func @decorator
def greet(name): """Says hello to somebody""" print(f"Hello, {name}!") print(greet.__name__)
# greet
print(greet.__doc__)
# Says hello to somebody
الغرض الوحيد من الوظيفة wraps
هو نسخ الاسم والوثائق وقائمة الوسائط وما إلى ذلك لمنع الكتابة فوقها. معتبرا أن wraps
إنه أيضًا مصمم ديكور ، يمكنك ببساطة إضافته إلى الوظيفة الفعلية ، وتم حل المشكلة!
تخفيض
أخيرًا وليس آخرًا في الأدوات الوظيفية للوحدة النمطية هو هذا التقليل. ربما من لغات أخرى ، قد تعرفها على أنها fold
(هاسكل). تأخذ هذه الوظيفة قابلة للتكرار وتطوي (تضيف) جميع قيمها في قيمة واحدة. هناك العديد من التطبيقات لذلك ، على سبيل المثال:
from functools import reduce
import operator def product(iterable): return reduce(operator.mul, iterable, 1) def factorial(n): return reduce(operator.mul, range(1, n)) def sum(numbers): # Use `sum` function from standard library instead return reduce(operator.add, numbers, 1) def reverse(iterable): return reduce(lambda x, y: y+x, iterable) print(product([1, 2, 3]))
# 6
print(factorial(5))
# 24
print(sum([2, 6, 8, 3]))
# 20
print(reverse("hello"))
# olleh
كما ترى من الكود ، reduce
يمكن تبسيط الشفرة أو تكثيفها في سطر واحد ، والذي قد يكون أطول من ذلك بكثير. مع ذلك ، عادة ما تكون فكرة سيئة إساءة استخدام هذه الوظيفة لمجرد تقليص الشفرة ، مما يجعلها "أكثر ذكاءً" ، حيث تصبح مخيفة وغير قابلة للقراءة بسرعة. لهذا السبب ، في رأيي ، يجب استخدامه باعتدال.
وإذا كنت تتذكر ذلك reduce
غالبًا ما يقصر كل شيء إلى سطر واحد ، ويمكن دمجه تمامًا مع partial
:
product = partial(reduce, operator.mul) print(product([1, 2, 3]))
# 6
وأخيرًا ، إذا كنت بحاجة إلى أكثر من مجرد النتيجة النهائية "المنهارة" ، فيمكنك استخدامها accumulate
- من وحدة أخرى رائعة itertools
. لحساب الحد الأقصى يمكن استخدامه على النحو التالي:
from itertools import accumulate data = [3, 4, 1, 3, 5, 6, 9, 0, 1] print(list(accumulate(data, max)))
# [3, 4, 4, 4, 5, 6, 9, 9, 9]
وفي الختام
كما ترى ، هناك العديد من الوظائف المفيدة وأدوات التزيين التي يمكن أن تجعل حياتك أسهل ، ولكن هذا مجرد غيض من فيض. كما قلت في البداية ، هناك العديد من الوظائف في مكتبة Python القياسية التي تساعدك على كتابة كود أفضل ، لذلك بالإضافة إلى الوظائف التي غطيناها هنا ، يمكنك الانتباه إلى وحدات أخرى ، مثل operator
or itertool.
لأية استفسارات ، يمكنك النقر فوق مربع التعليق الخاص بي. سأبذل قصارى جهدي لحل المدخلات الخاصة بك وآمل أن أقدم لك المخرجات المرجوة. يمكنك أيضًا التواصل معي على LinkedIn: -
https://www.linkedin.com/in/shivani-sharma-aba6141b6/
الوسائط الموضحة في هذه المقالة ليست مملوكة لشركة Analytics Vidhya ويتم استخدامها وفقًا لتقدير المؤلف.
مقالات ذات صلة
- '
- "
- 9
- إضافي
- خوارزمية
- الكل
- تحليلات
- التطبيقات
- الحجج
- البند
- مستحضرات التجميل
- أفضل
- صندوق
- دعوة
- تغيير
- الكود
- إحصاء
- خلق
- البيانات
- قاعدة البيانات
- إرسال
- إلخ
- أحداث
- أخيرا
- الاسم الأول
- تجمد
- وظيفة
- خير
- عظيم
- في المتناول
- هنا
- كيفية
- HTTPS
- فكرة
- معلومات
- IT
- حفظ
- اللغات
- المكتبة
- خط
- لينكدين:
- قائمة
- استماع
- طويل
- بحث
- القيام ب
- الرياضيات
- الوسائط
- أي
- أسماء
- أرقام
- حسنا
- عمليات
- مراجعة
- أخرى
- تجمع
- قوة
- برمجة وتطوير
- لغات البرمجة
- الملكية
- بايثون
- نادي القراءة
- تسجيل
- تخفيض
- REST
- النتائج
- عائدات
- علوم
- الاشارات
- حجم
- So
- حل
- بداية
- تجربه بالعربي
- الوقت
- us
- قيمنا
- في غضون
- كلمات
- للعمل
- أعمال
- العالم
- X