Python dynamic function attribute

I ran into an interesting problem trying to achieve dynamic sorting. Given the following code:

>>> l = []
>>> for i in range(2):
>>>     def f():
>>>         return f.v
>>>     f.v = i
>>>     l.append(f)

You should be careful how to use functions in l:

>>> l[0]()
1
>>> l[1]()
1
>>> [h() for h in l]
[1, 1]
>>> [f() for f in l]
[0, 1]
>>> f = l[0]
>>> f()
0
>>> k = l[1]
>>> k()
0
>>> f = l[1]
>>> k()
1
>>> del f
>>> k()
NameError: global name 'f' is not defined

The behavior of the function depends on what fis currently.

What should I do to avoid this problem? How to set a function attribute that is independent of the function name?

Update

Reading your comments and answers, here is my actual problem.

I have some data that I want to sort according to user input (so I don’t know the sorting criteria in advance). The user can choose on which part of the data to apply sequential sorts, and these varieties can be ascending or descending.

, , , , , sorted : key=lambda x: [f(x) for f in functions]. , ( ).

, , __call__.

+4
4

, return f.v f, , . 1 , :

>>> dis.dis(l[0])
  3           0 LOAD_GLOBAL              0 (f)
              3 LOAD_ATTR                1 (v)
              6 RETURN_VALUE

, l, f , :

>>> l
[<function f at 0x02594170>, <function f at 0x02594130>]
>>> f
<function f at 0x02594130>

, l[0](), f, , 1. f, f = l[0], f .

, , - , , . - :

class MyFunction:
  def __init__(self, v):
    self.v = v
  def __call__(self):
    return self.v

l = [MyFunction(i) for i in range(2)]

l[0]() # 0
l[1]() # 1

, .

1: f, , ?

, self, :

# ...
def my_method(self):
  return self.value

self - . Python , value. , , self. , :

a.value = 1
a.my_method()

self a.

, :

def f():
  return f.v

Python , f. , . .
, f.v = i, v f, , .

+4

, :

def f():
    return f.v

, v. f object v. f. , v " ". .

,

>>> f = l[0]
>>> k = l[1]
>>> k()
0

, , k l[1]. , , , f.v, , .

:

>>> k.v
1
>>> [h.v for h in l]
[0, 1]

, , , , ( , getattr(), ..). . ( @VincentSavard).

, , -, , "" ; , -, closure. , - ( @TomKarzes, lambda).

+1

:

l = []
for i in range(2):
    def f(n):
        return lambda: n
    l.append(f(i))

, i. n f. :

>>> [f() for f in l]
[0, 1]
0

As others have said, it return f.vsearches for a fname in the current area, which is equal to the last defined function.

To get around this, you can simulate functions:

>>> class Function(object):
...     def __init__(self, return_value):
...         self.return_value = return_value
...     def __call__(self):
...         return self.return_value
...     

>>> l = []

>>> for i in range(2):
...     l.append(Function(i))
...     

>>> l[0]()
>>> 0

>>> l[1]()
>>> 1
0
source

Source: https://habr.com/ru/post/1628143/


All Articles