2017-01-30 4 views
1

В JavaScript я мог бы написать такую ​​функцию:Есть ли более «питонический» способ написать это закрытие?

function counter() { 
    foo = 0; 
    function increment() { 
    foo += 1 
    console.log(foo); 
    } 
    function printVal() { 
    console.log(foo); 
    } 

    return { 
       increment: increment, 
      printVal: printVal, 
        }  
} 

func = counter(); 

func.increment() 
func.increment() 
func.printVal() 

Я хочу, чтобы попытаться достичь аналогичной функции с помощью питона. В частности, как я могу получить доступ к обеим внутренним функциям через возврат внешней функции.

Здесь работает, но смешно смотреть, версия в питоне:

def counter(): 
    foo = {'bar': 0} 
    def increment(): 
     foo['bar'] += 1 
     return foo['bar'] 
    def printVal(): 
     return foo['bar'] 
    return {'increment': increment, 'printVal': printVal} 

func = counter() 
func['increment']() 
func['printVal']() 

Есть ли какая-то более элегантного или «вещий» способ написания закрытия, как это?

+2

В этом случае вы, вероятно, захотите использовать объект 'collections.Counter' или просто написать класс. – monkut

+1

Вы должны спросить себя, почему вы используете закрытие в JS в первую очередь: закрытие часто заменяет поведение _classlike_. В случае python вы можете использовать действительно класс, чтобы моделировать поведение пользователя. И ваши две функции становятся _real_ методами. –

ответ

1

Просто реализовать __getitem__

class Foo: 
    class_attributes = {'a': 3, 
         'b': 5} 

    def __getitem__(self, name): 
     return Foo.class_attributes[name] 


f = Foo() 
print f['a'] 

выход:

3 
+0

Будут ли одинаковые атрибуты class_attributes использоваться для всех экземпляров Foo здесь? – fafl

+0

В этом примере это будет. Можно определить для каждого экземпляра class_attributes путем реализации конструктора. – AndreyF

0

Я думаю, что то, что вы хотите достичь, это то, как в следующем примере:

def makeInc(x): 
    def inc(y): 
    # x is "attached" in the definition of inc 
    return y + x 

    return inc 

incOne = makeInc(1) 
incFive = makeInc(5) 

incOne (5) # returns 6 
incFive(5) # returns 10 

Вы хотите вещий путь создания замыканий. Таким образом, приведенный выше пример демонстрирует, как это сделать.

3

Python не так силен, как другие языки в закрытии. Во-первых, он поддерживает только чтение «закрытых» переменных, а во-вторых, оператор return делает его немного более неудобным.

С другой стороны, это очень сжато с классами, так что если вы хотите, элемент данных с двумя функциями, я хотел бы сделать это с классом:

class Counter: 
    def __init__(self, c=0): 
     self.count = c 
    def increment(self): 
     self.count += 1 
    def printVal(self): 
     return self.count 


c = Counter() 
c.increment() 
print(c.printVal()) 

Я осмелюсь сказать, что в этом случае, это будет питонический путь.

EDIT

Осмотрев свой комментарий об отслеживании вызовов функций, я добавляю это. Вы можете сделать это путем закрытия, например:

# this is the tracked function 
def add2(a, b): 
    return a + b 

# this is the count tracker 
def counterize(func): 
    c = [0] 
    def counter_func(*args, **kw): 
     c[0] += 1 
     counter_func.count = c[0] 
     return func(*args, **kw) 
    return counter_func 


cadd2 = counterize(add2) 

print(cadd2(1, 2)) 
print(cadd2(3, 4)) 
print('Called %s times' % cadd2.count) 

>>> 
3 
7 
Called 2 times 

Но это не идиоматично в Python. Также сохранение count внутри объекта функции - хороший трюк, но это то, что есть. Трюк. С другой стороны, в LISP или Scala закрытие было бы более естественным, но тогда я не думаю, что можно было бы оставить count в качестве поля, но, вернее, вернуть его вместе с результатом.

Я бы сказал, что в этом случае, опять же, идиоматические коды Python будут через классы, он более приемлем ИМ и имеет ту же длину коды:

class Counterize: 
    def __init__(self, func): 
     self.func = func 
     self.count = 0 
    def __call__(self, *args, **kwargs): 
     self.count += 1 
     return self.func(*args, **kwargs) 


cadd2 = Counterize(add2) 
print(cadd2(1, 2)) 
print(cadd2(3, 4)) 
print('Called %s times' % cadd2.count) 

И выход будет правильным одна и та же. Цель __call__ - разрешить обработку объекта как функции посредством вызова с помощью скобок.

+0

Спасибо, я надеялся использовать закрытие, потому что я пытаюсь научиться более функциональным методам, но это похоже на лучшее решение. Большая картина того, что я хочу сделать, - это счетчик, который я могу использовать для отслеживания того, сколько раз функция вызывалась. –