Get the name of the Variable by which the function or class was called

Asked

Viewed 56 times

3

I was looking at how the databases are created in Sqlalchemy and I was in doubt in the part where I create class variables and are converted to a column in the database with the name of the variable used.

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite+pysqlite:///base.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)


class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)

    def __repr__(self):
        return f"<{self.username}>"

How can I create a function that does the same thing? that returns the name of the "Original Variable". Or turn the name of the "Original Variable" into a Kwargs like it happens when saving something in the database.

db.session.add(User(username="Something"))
db.session.commit()

1 answer

3


It’s not possible for common variables when they call a function (ok, even "it’s possible," but it’s much more complicated, and it’s not the intention for something like this to be done).

But for class attribute names - as is the case there, this is possible yes - the issue is that either the class has to have a special metaclass, or the content of the class attribute in the decoration has to be an object that meets the descriptor protocol (Descriptor Protocol) and have a method __set_name__.

So, to illustrate - this is possible:

class MinhaClasse:
    meu_atributo = ClasseEspecial()

m = MinhaClasse()
print(m.nome)
out-> 'meu_atributo'

And this is not possible (in those - "gives," but do not:):


def minha_funcao():
    minha_variavel = funcao_especial()
    print (minha_variavel.nome)

minha_funcao()
out-> 'minha_variavel'  # (Não tem como acontecer, a não ser com um hack muito grande)

So, focusing on what works and people are encouraged to use: if your class attribute is an object with a method __get__, it is a descriptor (a Descriptor) - and Python uses several legal mechanisms with it, including being able to determine the name automatically: At the moment the class is created, Python checks all class members that are descriptors, and, if they have the method __set_name__, it is called with two parameters: the newly created class, and the member’s own name as a string.

That is, for the first example above to work, one can use something like this:

class ClasseEspecial:
    def __get__(self, instance, owner):
        if instance is None: return self
        return instance.__dict__[self.name]
    def __set__(self, instance, value):
        instance.__dict__[self.name] = value
    def __set_name__(self, owner, name):
        self.name = name

class MinhaClasse:
    meu_atributo = ClasseEspecial()

And in interactive mode:


In [255]: MinhaClasse.meu_atributo.name                              
Out[255]: 'meu_atributo'

Why does it work that way? Why body of the classes is executed when a module is imported (in general) - when the block defined by the command class comes to an end, Python makes a series of internal calls, which receive as data all variables defined in the class body (the attributes), there the mechanism I described above comes into action and the method __set_name__ is called.

In a function, their body is only executed when they are called - the variables "don’t exist" until the function is running, and although the Python introspection mechanisms allow the program to "see" these variable names directly, this is not recoded: the ideal is that variable names are part of the structure of the program - if you need to handle a variable name as given (for example, a string), it is best to use a dictionary. And also that would be very useful - a variable in a function is only visible to the code within that function itself - unlike a class attribute that can be consulted from anywhere directly.

Just to make the answer complete, I will describe how a function that is called could know which variable is going to be stored in - but I won’t give an example in code:

There is an object type called "Frame" that maintains the state of a running function: it has as attributes the code being executed and the local variables, and the frme of the "parent" function - the function that called the current function. With this, a function when called can:

  1. get the execution frame itself
  2. from the frame itself, get the frame from the function where it was called
  3. in this frame, get the number of the line and point where the function is called (this information is also in the frame)
  4. obtain the code object (__code__): a sequence of bytes representing the bytecode that function
  5. look inside the bytecode which was the instruction where the function was called
  6. find from there the instructions that put the return value in a local variable
  7. obtain, in the code object, the name of the variable from its position

And, unlike what happens with class attributes, this will all work because the cPython reference implementation does so, and allows these accesses - but other Python implementations like Pypy, Brython, etc.... do not need (and do not implement) this in the same way, so it cannot work in any variant of the language.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.