2015-05-18 16 views
6

Zawsze uważałem, że funkcje Pythona 2.7 odnoszą się do zakresu, w jakim zostały zdefiniowane. Rozważmy następujący kod. Dlaczego drugie wyjście nie oznacza "obliczenia: sin"?Zrozumienie zamknięć w Pythonie

Czy istnieje sposób zmodyfikowania kodu, aby działał zgodnie z oczekiwaniami?

import math 

mymath = dict() 

for fun in ["sin", "cos"]: 
    def _impl(val): 
     print "calculating: %s" % fun 
     return getattr(math, fun)(val) 
    mymath[fun] = _impl 

# calculating: cos 
print mymath["cos"](math.pi) 

# calculating: cos <- why? 
print mymath["sin"](math.pi) 

Odpowiedz

7

Wartość fun jest obliczana przy wywołaniu funkcji.

W podanym przykładzie fun jest zmienną globalną, a jej wartością jest "cos" po uruchomieniu pętli for.

Wydaje mi się, że podczas tworzenia funkcji można zmienić wartość fun, ale tak nie jest. Funkcja ocenia wartość zmiennej, gdy działa tak, jak powinna.

Nie chodzi o przestrzeń nazw, w której definiujesz funkcję, ale o przestrzeń nazw, w której uruchamiasz tę funkcję.

import math 

mymath = dict() 

for fun in ["sin", "cos"]: 
    def _impl(val): 
     print "calculating: %s" % fun 
     return getattr(math, fun)(val) 
    mymath[fun] = _impl 


fun = 'tan' 
# will print and calculate tan 
print mymath["cos"](math.pi) 
2

Od tego kodu (który działa jak zamierzony)

my = {} 

def makefun(fun): 
    def _impl(x): 
    print fun, x 
    return _impl 

for fun in ["cos", "sin"]: 
    my[fun] = makefun(fun) 

# will print 'cos' 
my['cos'](1) 
fun = 'tan' 
# will print 'cos' 
my['cos'](2) 

wydaje się, że nie jest to przestrzeń nazw definicji funkcji, która decyduje o naturze zamknięcia ale zamiast przestrzeń nazw użyte zmienne. Więcej testowania:

my = dict() 

fun = '' 

def makefun(): 
    global fun #This line is switched on or off 
    fun = 'sin' 
    def _impl(x): 
    print fun, x 
    return _impl 

test = makefun() 

#gives sin 1 
test(1) 
fun = 'cos' 
#gives sin 2 if line global fun is used 
#gives cos 2 if line global fun is NOT used 
test(2) 

więc poprawne wyjaśnienie wydaje się, że zamknięcie zapisuje odwołanie do swoich argumentów, a nie wartość.

0

myślę, że staramy się te rzeczy trudniejsze: Oto w jaki sposób można to zrobić z zamknięciami:

import math 

mymath = dict() 


def funcmaker(fun): 
    print "creating %s function" % fun 
    def calculate(val): 
     print "calculating: %s" % fun 
     return getattr(math, fun)(val) 
    return calculate 

print funcmaker("sin")(math.pi) 
print funcmaker("cos")(math.pi) 

Powyższy kod daje następujący wynik:

creating sin function 
calculating: sin 
1.22464679915e-16 
creating cos function 
calculating: cos 
-1.0