Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

219
Visualizações
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 Respostas
Responde à pergunta

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 Relatório

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda