Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

220
Vistas
How can you add custom attributes to a python function object within the function declaration?

I'm looking to create a richly annotated codebase. I remembered functions in Python are objects, therefore they allow arbitrary attributes. For example:

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']}

I'm looking to set the __notes__ attribute from within the function definition. Any ideas on how this could be achieved?


EDIT: As pointed out in the comments, __notes__ is a poor name for this attribute. _notes is more appropriate.

over 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

You can use a decorator to attached the annotation after the function is defined.

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

As an aside, dunder names (like __notes__) are reserved for the implementation; you should not invent your own. Just use an ordinary "private" name prefixed with a single _.

over 4 years ago · Santiago Trujillo Denunciar

0

I'm going to answer somewhat differently. A function can refer to its own name in its body and can assign/read values to whatever it finds.

In your case, assuming you want to introspect what a function does, what you want to do is to drive function behavior from what's decorated on its object.

Note that in my example below adder3 - which does what you want and updates from within the function body is not documented from the POV of an introspection tool until it has been executed at least once.

adder2 on the other hand is set up so that it adjusts its behavior according to its annotation and that annotation is visible to introspection tools at all times.

adder4 is added to show one limit of this approach. Referring to the function's name is not a guarantee that you are in fact accessing the function, only whatever the function name resolves to in the globals.


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)}]")


output:


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]

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda