Estoy buscando crear una base de código ricamente anotada. Recordé que las funciones en Python son objetos, por lo que permiten atributos arbitrarios. Por ejemplo:
def foo(x:int, y:int)->int: '''This is a doc string''' return x**2 + y**2 foo.__notes__ = { 'Dependencies':['x', 'y'] } foo.__notes__ >>> {'Dependencies': ['x', 'y']} Estoy buscando establecer el atributo __notes__ desde dentro de la definición de la función. ¿Alguna idea sobre cómo podría lograrse esto?
EDITAR: Como se señaló en los comentarios, __notes__ es un nombre pobre para este atributo. _notes es más apropiado.
Puede usar un decorador para adjuntar la anotación después de definir la función.
def add_note(k, v): def _(f): if not hasattr(f, '_notes'): f._notes = {} f._notes[k] = v return f return _ @add_note('Dependencies', ['x', 'y']) def foo(x: int, y: int) -> int: return x**2 + y**2 Aparte, los nombres de dunder (como __notes__ ) están reservados para la implementación; usted no debe inventar el suyo propio. Simplemente use un nombre "privado" común con el prefijo _ .
Voy a responder algo diferente. Una función puede referirse a su propio nombre en su cuerpo y puede asignar/leer valores a lo que encuentre.
En su caso, suponiendo que desea introspeccionar lo que hace una función, lo que desea hacer es controlar el comportamiento de la función a partir de lo que está decorado en su objeto.
Tenga en cuenta que en mi ejemplo a continuación, adder3 , que hace lo que desea y se actualiza desde dentro del cuerpo de la función, no está documentado desde el punto de vista de una herramienta de introspección hasta que se haya ejecutado al menos una vez .
adder2 , por otro lado, está configurado para que ajuste su comportamiento de acuerdo con su anotación y esa anotación sea visible para las herramientas de introspección en todo momento.
adder4 se agrega para mostrar un límite de este enfoque. Hacer referencia al nombre de la función no es una garantía de que de hecho esté accediendo a la función, solo lo que el nombre de la función resuelva en los globales .
def adder1(x:int = 1, y: int = 2, z: int =3 )->int: '''This is a doc string''' return x**2 + y**2 def decorate(**kwds): """ assign kwds as attributes to the function object """ def actual_decorator(func): for k, v in kwds.items(): setattr(func, k, v) return func return actual_decorator depends = {"x","y"} @decorate(_note=f"depends:{depends}", depends=depends) def adder2(x:int = 1, y: int = 2, z: int =3 )->int: # a function can totally refer to its name # and use annotations to dynamically adjust its behavior depends = adder2.depends #sum up dependencies li = [] for key in depends: v = locals()[key] ** 2 li.append(v) return sum(li) def adder3(x:int = 1, y: int = 2, z: int =3 )->int: """ no note until function execution so this won't allow tools to inspect the module""" _note = getattr(adder3,"_note",None) if not _note: adder3._note = "depends x,y" return x**2 + y**2 def adder4(x:int = 1, y: int = 2, z: int =3 )->int: """there is no guarantee that referring by its own name works""" print("\n\nadder4.name:", adder4.__name__) return (x**2 + y**2) * 1000 adder4_saved = adder4 #swap name adder4 = adder2 for func in [adder1,adder2,adder3,adder4_saved]: print(f"\n{func.__name__} [note(before)={getattr(func,'_note',None)}] -> {func()} [note(after)={getattr(func,'_note',None)}]") adder1 [note(before)=None] -> 5 [note(after)=None] adder2 [note(before)=depends:{'y', 'x'}] -> 5 [note(after)=depends:{'y', 'x'}] adder3 [note(before)=None] -> 5 [note(after)=depends x,y] adder4.name: adder2 adder4 [note(before)=None] -> 5000 [note(after)=None]