logo

المؤشر في بايثون | لماذا لا تدعم بايثون المؤشر؟

في هذا البرنامج التعليمي، سوف نتعرف على المؤشر في بايثون ونرى لماذا لا تدعم بايثون مفاهيم المؤشر.

سوف نفهم أيضًا كيف يمكننا محاكاة المؤشر في بايثون. فيما يلي مقدمة للمؤشر لأولئك الذين ليس لديهم أي شيء عنه.

سوف نفهم أيضًا كيف يمكننا محاكاة المؤشر في بايثون. فيما يلي مقدمة للمؤشر لأولئك الذين ليس لديهم أي شيء عنه.

ما هو المؤشر؟

يعد المؤشر أداة شائعة ومفيدة للغاية لتخزين عنوان المتغير. إذا كان شخص ما قد عمل سابقًا بلغة منخفضة المستوى مثل ج . سي ++ ، فمن المحتمل أن يكون على دراية بالمؤشرات. إنه يدير الكود بكفاءة عالية. يمكن أن يكون الأمر صعبًا بعض الشيء بالنسبة للمبتدئين، ولكنه أحد المفاهيم المهمة للبرنامج. ومع ذلك، يمكن أن يؤدي ذلك إلى أخطاء مختلفة في إدارة الذاكرة. وهكذا تعريف المؤشرات -

'المؤشرات هي المتغيرات التي تحمل عنوان الذاكرة لمتغير آخر. يتم تمثيل متغيرات المؤشر بالعلامة النجمية (*).'

دعونا نرى المثال التالي للمؤشر في لغة البرمجة C.

مثال - كيفية استخدام المؤشر في لغة C

 #include int main() { int* po, o; 0 = 10; printf('Address of c: %p
', &c); printf('Value of c: %d

', c); o = &0; printf('Address of pointer pc: %p
', o); printf('Content of pointer pc: %d

', *o); 0 = 11; printf('Address of pointer pc: %p
', p0); printf('Content of pointer pc: %d

', *p0); *po = 2; printf('Address of c: %p
', &o); printf('Value of c: %d

', o); return 0; } 

انتاج:

Address of o: 2686784 Value of o: 22 Address of pointer po: 2686784 Content of pointer po: 22 Address of pointer po: 2686784 Content of pointer po: 11 Address of o: 2686784 Value of o: 2 

إلى جانب كونها مفيدة، لا يتم استخدام المؤشرات في بايثون . في هذا الموضوع، سنناقش نموذج الكائن في بايثون ونتعرف على سبب عدم وجود المؤشرات في بايثون. سوف نتعلم أيضًا طرقًا مختلفة لمحاكاة المؤشرات في بايثون. أولاً، دعونا نناقش سبب عدم دعم بايثون للمؤشرات.

لماذا لا تدعم لغة بايثون المؤشرات؟

السبب الدقيق لعدم دعم المؤشر غير واضح. هل يمكن أن يكون المؤشر في بايثون موجودًا أصلاً؟ المفهوم الرئيسي لبيثون هو بساطتها، ولكن المؤشر انتهك زين بايثون. يتم تشجيع المؤشرات بشكل أساسي على إجراء تغييرات ضمنية بدلاً من التغييرات الصريحة. كما أنها معقدة، خاصة بالنسبة للمبتدئين.

تميل المؤشرات إلى خلق تعقيد في التعليمات البرمجية، حيث تركز بايثون بشكل أساسي على سهولة الاستخدام بدلاً من السرعة. ونتيجة لذلك، لا تدعم بايثون المؤشر. ومع ذلك، تقدم بايثون بعض الفوائد لاستخدام المؤشر.

قبل أن نفهم المؤشر في بايثون، نحتاج إلى الحصول على الفكرة الأساسية للنقاط التالية.

  • كائنات غير قابلة للتغيير مقابل كائنات قابلة للتغيير
  • متغيرات/أسماء بايثون

الكائنات في بايثون

في بايثون، كل شيء عبارة عن كائن، حتى الفئة والوظائف والمتغيرات وما إلى ذلك. يحتوي كل كائن على ثلاث أجزاء من البيانات على الأقل.

مكرر جافا للخريطة
  • العد المرجعي
  • يكتب
  • قيمة

دعونا نناقش واحدا تلو الآخر.

عدد المراجع - يتم استخدامه لإدارة الذاكرة. للحصول على مزيد من المعلومات حول إدارة ذاكرة بايثون، اقرأ إدارة الذاكرة في بايثون.

يكتب - ال سي بايثون يتم استخدام الطبقة كنوع للتأكد من سلامة الكتابة أثناء وقت التشغيل. وأخيرًا، هناك قيمة، وهي القيمة الفعلية المرتبطة بالكائن.

إذا تعمقنا في هذا الكائن، فسنجد أن الكائنات ليست كلها متماثلة. التمييز المهم بين أنواع الكائنات هو غير قابل للتغيير وقابل للتغيير. أولاً، نحتاج إلى فهم الفرق بين أنواع الكائنات لأنه يستكشف المؤشر في بايثون.

كائنات غير قابلة للتغيير مقابل كائنات قابلة للتغيير

لا يمكن تعديل الكائنات غير القابلة للتغيير، حيث يمكن تعديل الكائنات القابلة للتغيير. دعونا نرى الجدول التالي للأنواع الشائعة وما إذا كانت قابلة للتغيير أم لا.

أشياء يكتب
كثافة العمليات غير قابل للتغيير
يطفو غير قابل للتغيير
بول غير قابل للتغيير
قائمة متقلب
تعيين متقلب
معقد متقلب
مترابطة بيانية غير قابل للتغيير
المجمدة غير قابل للتغيير
قاموس متقلب

يمكننا التحقق من نوع الكائنات المذكورة أعلاه باستخدام بطاقة تعريف() طريقة. تقوم هذه الطريقة بإرجاع عنوان ذاكرة الكائن.

نحن نكتب الأسطر أدناه في بيئة REPL.

 x = 5 id(x) 

انتاج:

140720979625920 

في الكود أعلاه، قمنا بتعيين القيمة 10 إلى x. إذا قمنا بتعديل هذه القيمة عن طريق الاستبدال، فسنحصل على الكائنات الجديدة.

 x-=1 id(x) 

انتاج:

140720979625888 

كما نرى، قمنا بتعديل الكود أعلاه وحصلنا على كائنات جديدة كاستجابة. ولنأخذ مثالا آخر على شارع .

 s = 'java' print(id(s)) s += 'Tpoint' print(s) id(s) 

انتاج:

2315970974512 JavaTpoint 1977728175088 

مرة أخرى، نقوم بتعديل قيمة x عن طريق إضافة سلسلة جديدة، ونحصل على عنوان الذاكرة الجديد. دعونا نحاول إضافة سلسلة مباشرة في s.

 s = 'java' s[0] = T print(id(s)) 

انتاج:

Traceback (most recent call last): File 'C:/Users/DEVANSH SHARMA/PycharmProjects/MyPythonProject/python1.py', line 34, in s[0] = T NameError: name 'T' is not defined 

يُرجع الكود أعلاه خطأ، مما يعني أن السلسلة لا تدعم الطفرة. لذا شارع هي الكائنات غير القابلة للتغيير.

الآن، سوف نرى الكائن القابل للتغيير مثل القائمة.

 my_list = [3, 4, 8] print(id(my_list)) my_list.append(4) print(my_list) print(id(my_list)) 

انتاج:

2571132658944 [3, 4, 8, 4] 2571132658944 

كما نرى في الكود أعلاه، فإن قائمتي لديه المعرف في الأصل، وقد أضفنا 5 إلى القائمة؛ قائمتي لديه نفس المعرف لأن القائمة تدعم التحولية.

فهم متغيرات بايثون

تختلف طريقة تعريف المتغيرات في Python كثيرًا عن C أو C++. متغير بايثون لا يحدد نوع البيانات. في الواقع، لدى بايثون أسماء، وليس متغيرات.

لذلك نحن بحاجة إلى فهم الفرق بين المتغيرات والأسماء، وخاصة عندما نتعامل مع موضوع المؤشرات الصعبة في بايثون.

مرقمة الأبجدية

دعونا نفهم كيف يعمل المتغير في لغة C وكيف يعمل الاسم في بايثون.

المتغيرات في ج

في لغة C، المتغير هو أنه يحمل قيمة أو قيمة مخزنة. يتم تعريفه بنوع البيانات. دعونا نرى الكود التالي الذي يحدد المتغير.

 int x = 286; 
  • تخصيص ذاكرة كافية لعدد صحيح.
  • نقوم بتعيين القيمة 286 لموقع الذاكرة هذا.
  • يمثل x تلك القيمة.

إذا كنا نمثل وجهة نظر الذاكرة -

المؤشر في بايثون

كما نرى، فإن x لديه موقع في الذاكرة للقيمة 286. الآن، سنقوم بتعيين القيمة الجديدة لـ x.

س = 250

تقوم هذه القيمة الجديدة بالكتابة فوق القيمة السابقة. وهذا يعني أن المتغير x قابل للتغيير.

موقع قيمة x هو نفسه، لكن القيمة تغيرت. وهي نقطة مهمة تشير إلى أن x هو موقع الذاكرة، وليس اسمها فقط.

الآن، نقدم المتغير الجديد الذي يأخذ x، ثم يقوم y بإنشاء صندوق الذاكرة الجديد.

 int y = x; 

يقوم المتغير y بإنشاء مربع جديد يسمى y، وينسخ قيمة x إلى المربع.

المؤشر في بايثون

الأسماء في بايثون

كما ناقشنا سابقًا، لا تحتوي لغة Python على المتغيرات. لها أسماء، ونحن نستخدم هذا المصطلح كمتغيرات. ولكن هناك فرق بين المتغيرات والأسماء. دعونا نرى المثال التالي.

 x = 289 

يتم تقسيم الكود أعلاه أثناء التنفيذ.

  1. قم بإنشاء كائن PyObject
  2. قم بتعيين رمز الكتابة على عدد صحيح لـ PyObject
  3. قم بتعيين القيمة إلى 289 لـ PyObject
  4. قم بإنشاء اسم يسمى x
  5. أشر x إلى PyObject الجديد
  6. قم بزيادة عدد مرات إعادة حساب PyObject بمقدار 1

وسوف تبدو على النحو التالي.

المؤشر في بايثون

يمكننا أن نفهم العمل الداخلي للمتغير في بايثون. يشير المتغير x إلى مرجع الكائن ولا يحتوي على مساحة الذاكرة كما كان من قبل. ويظهر أيضًا أن x = 289 يربط الاسم x بمرجع.

الآن، نقدم متغيرًا جديدًا ونخصص له x.

 y = x 

في بايثون، لن يقوم المتغير y بإنشاء الكائن الجديد؛ إنه مجرد اسم جديد يشير إلى نفس الكائن. الكائن إعادة العد كما زاد بمقدار واحد. يمكننا تأكيد ذلك على النحو التالي.

 y is x 

انتاج:

جافا فتح ملف
True 

إذا قمنا بزيادة قيمة y بمقدار واحد، فلن يعود ذلك يشير إلى نفس الكائن.

 y + =1 y is x 

وهذا يعني أننا في بايثون لا نقوم بتعيين متغيرات. وبدلاً من ذلك، نربط الأسماء بالمرجع.

محاكاة المؤشرات في بايثون

كما ناقشنا، بايثون لا تدعم المؤشر، ولكن يمكننا الحصول على فوائد استخدام المؤشر. توفر بايثون طرقًا بديلة لاستخدام المؤشر في بايثون. وترد أدناه هاتين الطريقتين.

  • استخدام الأنواع القابلة للتغيير كمؤشرات
  • استخدام كائنات بايثون المخصصة

دعونا نفهم النقاط المحددة.

استخدام الأنواع القابلة للتغيير كمؤشر

في القسم السابق، قمنا بتعريف كائنات النوع القابلة للتغيير؛ يمكننا التعامل معها كما لو كانت مؤشرات لمحاكاة سلوك المؤشر. دعونا نفهم المثال التالي.

ج

 void add_one(int *a) { *a += 1; } 

في الكود أعلاه، قمنا بتعريف المؤشر *a، ثم قمنا بزيادة القيمة بمقدار واحد. الآن، سوف نقوم بتنفيذ ذلك باستخدام الدالة main().

مقسمة بواسطة سلسلة Java
 #include int main(void) { int y = 233; printf('y = %d
', y); add_one(&y); printf('y = %d
', y); return 0; } 

انتاج:

y = 233 y = 234 

يمكننا محاكاة هذا النوع من السلوك باستخدام نوع Python القابل للتغيير. فهم المثال التالي.

 def add_one(x): x[0] += 1 y = [2337] add_one(y) y[0] 

تصل الوظيفة المذكورة أعلاه إلى العنصر الأول في القائمة وتزيد قيمته بمقدار واحد. عندما نقوم بتنفيذ البرنامج أعلاه، فإنه يطبع القيمة المعدلة لـ y. هذا يعني أنه يمكننا تكرار المؤشر باستخدام الكائن القابل للتغيير. ولكن إذا حاولنا محاكاة المؤشر باستخدام كائن غير قابل للتغيير.

 z = (2337,) add_one(z) 

انتاج:

Traceback (most recent call last): File '', line 1, in File '', line 2, in add_one TypeError: 'tuple' object does not support item assignment 

استخدمنا الصف في الكود أعلاه، وهو كائن غير قابل للتغيير، لذا أعاد الخطأ. يمكننا أيضًا استخدام القاموس لمحاكاة المؤشر في بايثون.

دعونا نفهم المثال التالي حيث سنقوم بإحصاء كل عملية تحدث في البرنامج. يمكننا استخدام dict لتحقيق ذلك.

مثال -

 count = {'funcCalls': 0} def car(): count['funcCalls'] += 1 def foo(): count['funCcalls'] += 1 car() foo() count['funcCalls'] 

انتاج:

2 

توضيح -

في المثال أعلاه، استخدمنا عدد القاموس الذي يتتبع عدد استدعاءات الوظائف. عندما فو () تم استدعاء الدالة، وتم زيادة العداد بمقدار 2 لأن الإملاء قابل للتغيير.

استخدام كائنات بايثون

في المثال السابق، استخدمنا dict لمحاكاة المؤشر في بايثون، ولكن في بعض الأحيان يصبح من الصعب تذكر جميع أسماء المفاتيح المستخدمة. يمكننا استخدام فئة Python المخصصة بدلاً من القاموس. دعونا نفهم المثال التالي.

مثال -

 class Pointer(object): def __init__(self): self._metrics = { 'funCalls': 0, 'catPictures': 0, } 

في الكود أعلاه، قمنا بتعريف فئة المؤشر. استخدمت هذه الفئة الإملاء للاحتفاظ بالبيانات الفعلية في متغير العضو _metrics. وسوف توفر التغيير لبرنامجنا. يمكننا القيام بذلك على النحو التالي.

 class Pointer(object): # ... @property def funCalls(self): return self._metrics['func_calls'] @property def catPictures_served(self): return self._metrics['cat_pictures_served'] 

وقد استخدمنا @ملكية مصمم ديكور. إذا لم تكن على دراية بمصممي الديكور، فتفضل بزيارة البرنامج التعليمي لمصممي Python. سيتمكن مصمم الديكور @property من الوصول إلى funCalls وcatPicture_served. الآن، سوف نقوم بإنشاء كائن من فئة المؤشر.

 pt = Pointer() pt.funCalls() pt.catPicture_served 

هنا نحن بحاجة إلى زيادة هذه القيم.

 class Pointer(object): # ... def increament(self): self._metrices['funCalls'] += 1 def cat_pics(self): self._metrices['catPictures_served'] += 1 

لقد حددنا طريقتين جديدتين - increment() وcat_pics(). لقد قمنا بتعديل القيم باستخدام هذه الوظائف في إملاء المصفوفات. هنا، يمكننا تغيير الفئة بنفس الطريقة التي نعدل بها المؤشر.

 pt = Pointer() pt.increment() pt.increment() pt.funCalls() 

وحدة بايثون ctypes

تتيح لنا وحدة Python ctypes إنشاء مؤشر من النوع C في Python. هذه الوحدة مفيدة إذا أردنا إجراء استدعاء دالة لمكتبة C التي تتطلب مؤشرًا. دعونا نفهم المثال التالي.

مثال - لغة C

 void incr_one(int *x) { *x += 1; } 

في الدالة أعلاه، قمنا بزيادة قيمة x بمقدار واحد. لنفترض أننا حفظنا الملف أعلاه المسمى incrPointer.c واكتب الأمر التالي في الجهاز.

 $ gcc -c -Wall -Werror -fpic incrPointer.c $ gcc -shared -o libinc.so incrPointer.o 

الأمر الأول يجمع incrPointer.c في كائن يسمى incrPointer.o. يقبل الأمر الثاني ملف الكائن وينتج libinic.so للتعاون مع ctypes.

js المتغير العالمي
 import ctypes ## libinc.so library should be same directory as this program lib = ctypes.CDLL('./libinc.so') lib.increment 

انتاج:

 

في الكود أعلاه، ctypes.CDLL إرجاع كائن مشترك يسمى libinic.so. أنه يحتوي على إنكربوينتير() وظيفة. إذا أردنا تحديد المؤشر للوظائف التي نحددها في كائن مشترك، فيجب علينا تحديده باستخدام أنواع ctypes. دعونا نرى المثال أدناه.

 inc = lib.increment ## defining the argtypes inc.argtypes = [ctypes.POINTER(ctypes.c_int)] 

إذا قمنا باستدعاء الدالة باستخدام نوع مختلف، فسيتم ذلك من خلال خطأ.

 incrPointer(10) 

انتاج:

Traceback (most recent call last): File '', line 1, in ctypes.ArgumentError: argument 1: : expected LP_c_int instance instead of int 

وذلك لأن incrPointer يتطلب مؤشرًا وctypes هي طريقة لتمرير المؤشر في Python.

 v = ctypes.c_int(10) 

v هو متغير C. توفر ctypes الطريقة التي تسمى بايرف () والتي تستخدم لتمرير المرجع المتغير.

 inc(ctypes.byref(a)) a 

انتاج:

c_int(11) 

لقد قمنا بزيادة القيمة باستخدام المتغير المرجعي.

خاتمة

لقد ناقشنا أن المؤشر غير موجود في بايثون، لكن يمكننا تنفيذ نفس السلوك مع الكائن * القابل للتغيير. ناقشنا أيضًا وحدات ctypes التي يمكنها تعريف مؤشر C في Python. لقد حددنا بعض الطرق الممتازة لمحاكاة المؤشر في بايثون.