سؤال

أرغب في محاكاة سلوك التمرير بالقيمة في بيثون.بمعنى آخر، أود التأكد تمامًا من أن الوظيفة التي أكتبها لا تعدل البيانات المقدمة من المستخدم.

إحدى الطرق الممكنة هي استخدام نسخة عميقة:

from copy import deepcopy
def f(data):
    data = deepcopy(data)
    #do stuff

هل هناك أكثر كفاءة أو أكثر بيثونية طريقة لتحقيق هذا الهدف، من خلال وضع أقل عدد ممكن من الافتراضات حول الكائن الذي يتم تمريره (مثل طريقة .clone())

يحرر

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

هل كانت مفيدة؟

المحلول

لا توجد طريقة بيثونية للقيام بذلك.

توفر بايثون عددًا قليلاً جدًا من التسهيلات لـ إنفاذ أشياء مثل البيانات الخاصة أو للقراءة فقط.الفلسفة الثعبانية هي أننا جميعًا بالغون متراضيون:في هذه الحالة، هذا يعني أن "الوظيفة لا ينبغي أن تغير البيانات" هي جزء من المواصفات ولكن لا يتم فرضها في التعليمات البرمجية.


إذا كنت تريد عمل نسخة من البيانات، فإن الحل الأقرب الذي يمكنك الحصول عليه هو الحل الذي يناسبك.لكن copy.deepcopy, ، إلى جانب كونها غير فعالة، لديها أيضًا محاذير مثل:

نظرًا لأن النسخة العميقة تنسخ كل شيء، فقد تنسخ كثيرًا، على سبيل المثال، هياكل البيانات الإدارية التي يجب مشاركتها حتى بين النسخ.

[...]

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

لذلك أوصي به فقط إذا كنت تعلم أنك تتعامل مع أنواع Python المضمنة أو الكائنات الخاصة بك (حيث يمكنك تخصيص سلوك النسخ عن طريق تحديد __copy__ / __deepcopy__ أساليب خاصة، ليست هناك حاجة لتحديد بنفسك clone() طريقة).

نصائح أخرى

يمكنك إنشاء ديكور ووضع سلوك الاستنساخ فيه.

>>> def passbyval(func):
def new(*args):
    cargs = [deepcopy(arg) for arg in args]
    return func(*cargs)
return new

>>> @passbyval
def myfunc(a):
    print a

>>> myfunc(20)
20

هذه ليست الطريقة الأقوى، ولا تتعامل مع وسيطات القيمة الرئيسية أو أساليب الفصل (نقص الوسيطة الذاتية)، لكنك حصلت على الصورة.

لاحظ أن العبارات التالية متساوية:

@somedecorator
def func1(): pass
# ... same as ...
def func2(): pass
func2 = somedecorator(func2)

يمكنك أيضًا أن تجعل مصمم الديكور يأخذ نوعًا من الوظيفة التي تقوم بالاستنساخ وبالتالي تسمح لمستخدم مصمم الديكور بتحديد استراتيجية الاستنساخ.في هذه الحالة، من الأفضل تنفيذ الديكور كفصل دراسي __call__ تم تجاوزه.

لا يوجد سوى عدد قليل من الأنواع المضمنة التي تعمل كمراجع، مثل list, ، على سبيل المثال.

لذلك، بالنسبة لي، فإن طريقة لغة بايثون للقيام بتمرير القيمة، للقائمة، في هذا المثال، ستكون:

list1 = [0,1,2,3,4]
list2 = list1[:]

list1[:] ينشئ مثيلًا جديدًا للقائمة 1، ويمكنك تعيينه لمتغير جديد.

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

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

على أي حال...نأمل أن يساعد.

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

لا أستطيع معرفة أي شيء آخر بيثونية خيار.لكن شخصياً أفضّل المزيد س طريق.

class TheData(object):
  def clone(self):  """return the cloned"""

def f(data):
  #do stuff

def caller():
  d = TheData()
  f(d.clone())

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

import pickle

def f(data):
    data = pickle.loads(pickle.dumps((data)))
    #do stuff

بالإضافة إلى المستخدم695800 الإجابة، قم بالتمرير حسب القيمة للقوائم الممكنة باستخدام عامل التشغيل [:].

def listCopy(l):
    l[1] = 5
    for i in l:
        print i

دعا مع

In [12]: list1 = [1,2,3,4]

In [13]: listCopy(list1[:])
1
5
3
4

list1
Out[14]: [1, 2, 3, 4]
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top