ما هي القيود المفروضة على عمليات الإغلاق في بايثون مقارنة بإغلاقات اللغة X؟

StackOverflow https://stackoverflow.com/questions/141642

  •  02-07-2019
  •  | 
  •  

سؤال

حيث X هي أي لغة برمجة (C#، Javascript، Lisp، Perl، Ruby، Scheme، إلخ) التي تدعم بعض عمليات الإغلاق.

تم ذكر بعض القيود في الإغلاقات في بايثون (مقارنة بإغلاقات روبي)، لكن المقالة قديمة والعديد من القيود لم تعد موجودة في بايثون الحديثة.

سيكون من الرائع رؤية مثال للتعليمات البرمجية لقيود محددة.

أسئلة ذات صلة:

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

المحلول

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

>>> def outer(x): 
...     def inner_reads():
...         # Will return outer's 'x'.
...         return x
...     def inner_writes(y):
...         # Will assign to a local 'x', not the outer 'x'
...         x = y
...     def inner_error(y):
...         # Will produce an error: 'x' is local because of the assignment,
...         # but we use it before it is assigned to.
...         tmp = x
...         x = y
...         return tmp
...     return inner_reads, inner_writes, inner_error
... 
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
5
>>> inner_error(10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in inner_error
UnboundLocalError: local variable 'x' referenced before assignment

الاسم الذي يتم تعيينه في نطاق محلي (دالة) يكون دائمًا محليًا، ما لم يتم الإعلان عن خلاف ذلك.في حين أن هناك إعلانًا "عالميًا" للإعلان عن متغير عالمي حتى عندما يتم تعيينه إليه، إلا أنه لا يوجد مثل هذا الإعلان للمتغيرات المغلقة - حتى الآن.في Python 3.0، يوجد (سيكون) الإعلان "غير المحلي" الذي يفعل ذلك تمامًا.

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

>>> def outer(x):
...     x = [x]
...     def inner_reads():
...         # Will return outer's x's first (and only) element.
...         return x[0]
...     def inner_writes(y):
...         # Will look up outer's x, then mutate it.      
...         x[0] = y
...     def inner_error(y):
...         # Will now work, because 'x' is not assigned to, just referenced.
...         tmp = x[0]
...         x[0] = y
...         return tmp
...     return inner_reads, inner_writes, inner_error
... 
>>> inner_reads, inner_writes, inner_error = outer(5)
>>> inner_reads()
5
>>> inner_writes(10)
>>> inner_reads()
10
>>> inner_error(15)
10
>>> inner_reads()
15

نصائح أخرى

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

def outer ():
    x = 1
    def inner ():
        print x
        x = 2
    return inner
outer () ()

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

أحد القيود (أو "القيود") لإغلاقات بايثون، مقارنةً بإغلاقات جافا سكريبت، هو أنها لا تستطيع استخدامها لفعالية إخفاء البيانات

جافا سكريبت

var mksecretmaker = function(){
    var secrets = [];
    var mksecret = function() {
        secrets.push(Math.random())
    }
    return mksecret
}
var secretmaker = mksecretmaker();
secretmaker(); secretmaker()
// privately generated secret number list
// is practically inaccessible

بايثون

import random
def mksecretmaker():
    secrets = []
    def mksecret():
        secrets.append(random.random())
    return mksecret

secretmaker = mksecretmaker()
secretmaker(); secretmaker()
# "secrets" are easily accessible,
# it's difficult to hide something in Python:
secretmaker.__closure__[0].cell_contents # -> e.g. [0.680752847190161, 0.9068475951742101]

تم إصلاحه في Python 3 عبر nonlocal إفادة:

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

@ جون ميليكين

def outer():
    x = 1 # local to `outer()`

    def inner():
        x = 2     # local to `inner()`
        print(x)
        x = 3
        return x

    def inner2():
        nonlocal x
        print(x)  # local to `outer()`
        x = 4     # change `x`, it is not local to `inner2()`
        return x

    x = 5         # local to `outer()`
    return (inner, inner2)

for inner in outer():
    print(inner()) 

# -> 2 3 5 4

تعليق ل @ إجابة كيفن ليتل لتضمين مثال التعليمات البرمجية

nonlocal لا يحل هذه المشكلة تمامًا على python3.0:

x = 0 # global x
def outer():
    x = 1 # local to `outer`
    def inner():
        global x
        x = 2 # change global
        print(x) 
        x = 3 # change global
        return x
    def inner2():
##        nonlocal x # can't use `nonlocal` here
        print(x)     # prints global
##        x = 4      # can't change `x` here
        return x
    x = 5
    return (inner, inner2)

for inner in outer():
    print(inner())
# -> 2 3 3 3

على الجانب الآخر:

x = 0
def outer():
    x = 1 # local to `outer`
    def inner():
##        global x
        x = 2
        print(x) # local to `inner` 
        x = 3 
        return x
    def inner2():
        nonlocal x
        print(x)
        x = 4  # local to `outer`
        return x
    x = 5
    return (inner, inner2)

for inner in outer():
    print(inner())
# -> 2 3 5 4

يعمل على بيثون3.1-3.3

الحل الأفضل حتى الإصدار 3.0 هو تضمين المتغير كمعلمة افتراضية في تعريف الدالة المرفقة:

def f()
    x = 5
    def g(y, z, x=x):
        x = x + 1
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top