4
votes

Comment savoir si une fonction a été déclarée par `lambda` ou` def`?

Si je déclare deux fonctions a et b:

>>> a.__name__ = '<lambda>'
>>> is_lambda_function(a)
True

je ne peux pas utiliser type pour les différencier, car ils sont tous les deux du même type.

def is_lambda_function(function):
    return function.__name__ == "<lambda>"

>>> is_lambda_function(a)
False
>>> is_lambda_function(b)
True

De plus, types.LambdaType n'aide pas:

XXX

On pourrait utiliser __name__ comme:

>>> import types
>>> isinstance(a, types.LambdaType)
True
>>> isinstance(b, types.LambdaType)
True

Cependant, puisque __name__ pourrait ont été modifiés, is_lambda_function n'est pas garanti de retourner le résultat correct:

assert type(a) == type(b)

Y a-t-il un moyen qui produit un résultat plus fiable que le __ name__ attribut?


10 commentaires

Y a-t-il un cas d'utilisation pratique où vous en auriez réellement besoin?


Il peut également y avoir des objets qui ne sont ni mais appelables, et des fonctions intégrées Python.


Avec l'autre commentaire ci-dessus, quelle différence cela fait-il qu'il s'agisse d'un def ou d'un lambda?


@JaredSmith, par exemple Django ne peut pas sérialiser Lambdas.


@jpp: IMHO ce n'est pas un doublon! La réponse acceptée pour l'autre question fait clairement référence à Python2 tandis que Python3 est plus couramment utilisé de nos jours. Et il ne peut pas être appliqué en Python3 ...


@jpp stackoverflow.com/a/23852434/521776 cette réponse s'applique également à py3


Quel est votre cas d'utilisation?


Il est difficile de le savoir sans votre cas d'utilisation, mais vous pouvez probablement éviter cela, c'est-à-dire essayer quelque chose qui ne fonctionne qu'avec les fonctions def, sauf l'exception soulevée spécifique et continuer comme vous vouloir


@Kanak Dans ce cas: essayez de le sérialiser avec Django?


En relation: stackoverflow.com/questions/12264834/...


3 Réponses :


5
votes

AFAIK, vous ne pouvez pas être fiable en Python 3.

Python 2 utilisé pour définir un tas de types de fonctions. Pour cette raison, les méthodes, les lambdas et les fonctions simples ont chacun leur propre type.

Python 3 n'a qu'un seul type qui est function . Il existe en effet différents effets secondaires où déclarer une fonction régulière avec def et un lambda : def définit le nom sur le nom (et le nom qualifié) de la fonction et peut définir une docstring, tandis que lambda définit le nom (et le nom qualifié) sur , et définit la docstring sur None. Mais comme cela peut être changé ...

Si les fonctions sont chargées depuis une source Python ordinaire (et non tapées dans un environnement interactif), le module inspect permet d'accéder au code Python original:

>>> g = lambda x: 3*x
>>> g.__qualname__ = "g"
>>> pickle.dumps(g)
b'\x80\x03c__main__\ng\nq\x00.'

Cela affichera:

<function f at 0x00000253957B7840> False
<function g at 0x00000253957B78C8> True

Au fait, si le problème était la sérialisation de la fonction, une fonction déclarée comme lambda peut être décapée avec succès, à condition que vous lui donniez un nom qualifié unique:

import inspect

def f(x):
    return x**2

g = lambda x: x**2

def is_lambda_func(f):
    """Tests whether f was declared as a lambda.

Returns: True for a lambda, False for a function or method declared with def
Raises:
    TypeError if f in not a function
    OSError('could not get source code') if f was not declared in a Python module
                                         but (for example) in an interactive session
"""
    if not inspect.isfunction(f):
        raise TypeError('not a function')
    src = inspect.getsource(f)
    return not src.startswith('def') and not src.startswith('@') # provision for decorated funcs

g.__name__ = 'g'
g.__qualname__ = 'g'

print(f, is_lambda_func(f))
print(g, is_lambda_func(g))


0 commentaires

1
votes

J'ai profité de l'occasion pour plonger dans source de cpython pour voir si je pouvais trouver quelque chose, et je J'ai peur de devoir revenir sur la réponse de Serge : vous ne pouvez pas.

En bref, voici le parcours d'un lambda dans l'interprète:

  1. Lors de l'analyse syntaxique, lambdas, comme toute autre expression, sont lus dans un expr_ty , qui est une énorme union contenant les données de chaque expression.
  2. Cette expr_ty est ensuite convertie dans le type approprié ( Lambda , dans notre cas)
  3. Après un certain temps, nous atterrissons dans la fonction qui compil
  4. Cette fonction appelle ass , qui appelle makecode , qui initialise un PyCodeObject (fonctions, méthodes, ainsi que lambdas, tous finissent ici ).

De cela, je ne vois rien de particulier qui soit spécifique aux lambdas. Ceci, combiné au fait que Python vous permet de modifier à peu près tous les attributs des objets, me / nous fait croire que ce que vous voulez faire n'est pas possible.


1 commentaires

Pour amplifier votre réponse: puisque def et lambda produisent des objets indiscernables (d'autant plus que vous pouvez librement modifier __name__ et __qualname__ ), non seulement il est "impossible" de faire ce que OP demande, mais ce n'est pas significatif . Comment faire la différence entre une liste produite par une compréhension de liste et une liste créée par la fonction list et remplie par une boucle for ? Comment faire la différence entre une chaîne produite par des guillemets simples et une chaîne produite par des guillemets doubles? Vous ne pouvez pas, car il n'y a aucune différence , sauf au niveau du code source.



1
votes

Vous pouvez vérifier __code __. co_name . Il contient le nom au moment de la compilation de la fonction / lambda:

>>> is_lambda_function(a)
False
>>> is_lambda_function(b)
True

Et, contrairement à __name__ , __code __. Co_name est en lecture seule ...

>>> a.__name__ = "<lambda>"
>>> b.__name__ = "b"

>>> a.__code__.co_name = "<lambda>"
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: readonly attribute

>>> b.__code__.co_name = "b"
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: readonly attribute

... pour que les résultats restent les mêmes:

def a(x):
    return x**2

b = lambda x: x**2

def is_lambda_function(f):
    return f.__code__.co_name == "<lambda>"

>>> is_lambda_function(a)
False
>>> is_lambda_function(b)
True


0 commentaires