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

497
Visualizações
How can I get the attribute name when working with descriptor protocol in Python?

The descriptor protocol works fine but I still have one issue I would like to resolve.

I have a descriptor:

class Field(object):
    def __init__(self, type_, name, value=None, required=False):
        self.type = type_
        self.name = "_" + name
        self.required = required
        self._value = value

    def __get__(self, instance, owner):
        return getattr(instance, self.name, self.value)

    def __set__(self, instance, value):
        if value:
            self._check(value)
            setattr(instance, self.name, value)
        else:
            setattr(instance, self.name, None)

    def __delete__(self, instance):
        raise AttributeError("Can't delete attribute")

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, value):
        self._value = value if value else self.type()

    @property
    def _types(self):
        raise NotImplementedError

    def _check(self, value):
        if not isinstance(value, tuple(self._types)):
            raise TypeError("This is bad")

This is subclassed:

class CharField(Field):
    def __init__(self, name, value=None, min_length=0, max_length=0, strip=False):
        super(CharField, self).__init__(unicode, name, value=value)
        self.min_length = min_length
        self.max_length = max_length
        self.strip = strip

    @property
    def _types(self):
        return [unicode, str]

    def __set__(self, instance, value):
        if self.strip:
            value = value.strip()

        super(CharField, self).__set__(instance, value)

And then used is a model class:

class Country(BaseModel):
    name = CharField("name")
    country_code_2 = CharField("country_code_2", min_length=2, max_length=2)
    country_code_3 = CharField("country_code_3", min_length=3, max_length=3)

    def __init__(self, name, country_code_2, country_code_3):
        self.name = name
        self.country_code_2 = country_code_2
        self.country_code_3 = country_code_3

So far, so good, this works just as expected.

The only issue I have here is that we have to give a field name every time a field is declared. e.g. "country_code_2" for the country_code_2 field.

How would it be possible to get the attribute name of the model class and use it in the field class?

over 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

There is a simple way, and there is a hard way.

The simple way is to use Python 3.6 (or newer), and give your descriptor an additional object.__set_name__() method:

def __set_name__(self, owner, name):
    self.name = '_' + name

When a class is created, Python automatically will call that method on any descriptors you set on the class, passing in the class object and the attribute name.

For earlier Python versions, the best next option is to use a metaclass; it'll be called for every subclass that is created, and given a handy dictionary mapping attribute name to attribute value (including you descriptor instances). You can then use this opportunity to pass that name to the descriptor:

class BaseModelMeta(type):
    def __new__(mcls, name, bases, attrs):
        cls = super(BaseModelMeta, mcls).__new__(mcls, name, bases, attrs)
        for attr, obj in attrs.items():
            if isinstance(obj, Field):
                obj.__set_name__(cls, attr)
        return cls

This calls the same __set_name__() method on the field, that Python 3.6 supports natively. Then use that as the metaclass for BaseModel:

class BaseModel(object, metaclass=BaseModelMeta):
    # Python 3

or

class BaseModel(object):
    __metaclass__ = BaseModelMeta
    # Python 2

You could also use a class decorator to do the __set_name__ calls for any class you decorate it with, but that requires you to decorate every class. A metaclass is automatically propagated through the inheritance hierarchy instead.

over 4 years ago · Santiago Trujillo Relatório

0

I go through this in my book, Python Descriptors, though I haven't updated to a second edition to add the new feature in 3.6. Other than that, it's a fairly comprehensive guide on descriptors, taking 60 pages on just the one feature.

Anyway, a way to get the name without metaclasses is with this very simple function:

def name_of(descriptor, instance):
    attributes = set()
    for cls in type(instance).__mro__:
        # add all attributes from the class into `attributes`
        # you can remove the if statement in the comprehension if you don't want to filter out attributes whose names start with '__'
        attributes |= {attr for attr in dir(cls) if not attr.startswith('__')}
    for attr in attributes:
        if type(instance).__dict__[attr] is descriptor:
            return attr

Considering every time you use the name of the descriptor, the instance is involved, this shouldn't be too difficult to figure out how to use. You could also find a way to cache the name once you've looked it up the first time.

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