Como adicionar propriedade a uma classe dinamicamente?

215

O objetivo é criar uma classe simulada que se comporte como um conjunto de resultados db.

Por exemplo, se uma consulta ao banco de dados retornar, usando uma expressão dict,, {'ab':100, 'cd':200}eu gostaria de ver:

>>> dummy.ab
100

No começo, pensei que talvez pudesse fazê-lo desta maneira:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

mas c.abretorna um objeto de propriedade.

Substituir a setattrlinha por k = property(lambda x: vs[i])não serve para nada.

Então, qual é o caminho certo para criar uma propriedade de instância em tempo de execução?

PS Conheço uma alternativa apresentada em Como o __getattribute__método é usado?

Anthony Kong
fonte
2
Existem alguns erros de digitação no seu código: a definição de fn_readonly precisa de a :e de __init__referências self.fn_readyonly.
Mhawke
Você está certo. Eu adicionei essa função setter no último minuto para sublinhar o motivo da criação de uma propriedade em tempo de execução.
Anthony Kong
O principal problema que tive com a criação de propriedades na inicialização foi que, em alguns casos, se eu chamasse os auxiliares rapidamente depois ou houvesse um problema, eu receberia um erro de que eles não existiam, apesar do fato de existirem. Na minha solução abaixo, eu crio 2 classes. Um como Base / Pai (que estou tentando encontrar uma solução para evitar) e o objeto principal, que estende o Base / Pai. Então, no objeto principal, sem inicializar, chamo meu criador AccessorFunc, que cria as propriedades, funções auxiliares e muito mais.
Acecool 27/01
ou seja: classe ExampleBase: pass; classe Exemplo (Base de Exemplo): __x = Acessador (Base de Exemplo, 'x', 'X', 123); --- que criaria uma propriedade sob x e nomearia funções usando X, portanto, GetX, SetX e mais ... e .x, ._x e .__ x para a propriedade. Portanto, .x é a própria propriedade para a passagem dos dados (obtendo / configurando via self.x = 123; ou self.x para saída). Usei self._x para os dados RAW armazenados, para que pudessem ser facilmente acessíveis, pois também permiti que valores padrão fossem atribuídos, sem defini-los nos dados armazenados. assim _x poderia ser None e .x poderia retornar 123. e .__ x vinculado ao
acessador
Aqui está um link para a versão básica que cria propriedades dinâmicas e funções dinâmicas - o arquivo possui vários links para outras versões. Um é o sistema AccessorFunc, que usa uma função para criar auxiliares (um para funções, um para propriedades, um para ambos como elementos individuais - para que ele não use a redução de código em nada nesse arquivo). Se algo estiver faltando, um dos os outros arquivos o possuem: dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0
Acecool

Respostas:

333

Suponho que deveria expandir essa resposta, agora que sou mais velho, mais sábio e sei o que está acontecendo. Antes tarde do que nunca.

Você pode adicionar uma propriedade a uma classe dinamicamente. Mas esse é o problema: você deve adicioná-lo à classe .

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

A propertyé na verdade uma implementação simples de uma coisa chamada descritor . É um objeto que fornece tratamento personalizado para um determinado atributo, em uma determinada classe . Como uma maneira de ifseparar uma árvore enorme __getattribute__.

Quando eu pedir foo.b, no exemplo acima, Python vê que o bdefinido na classe implementa o protocolo de descritor -que significa apenas que é um objeto com um __get__, __set__ou __delete__método. O descritor assume a responsabilidade de manipular esse atributo, então o Python chama Foo.b.__get__(foo, Foo), e o valor de retorno é passado de volta para você como o valor do atributo. No caso de property, cada um desses métodos apenas chama a fget, fsetou fdelvocê passado para o propertyconstrutor.

Os descritores são realmente a maneira do Python de expor o encanamento de toda a sua implementação OO. De fato, há outro tipo de descritor ainda mais comum que property.

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

O método humilde é apenas outro tipo de descritor. Sua __get__aderência à instância de chamada como o primeiro argumento; de fato, faz isso:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

De qualquer forma, suspeito que seja por isso que os descritores só funcionam em classes: eles são uma formalização do material que alimenta as classes em primeiro lugar. Eles são até a exceção à regra: você pode obviamente atribuir descritores a uma classe, e as próprias classes são instâncias type! De fato, tentar ler Foo.bainda chamadas property.__get__; é apenas idiomático que os descritores retornem a si mesmos quando acessados ​​como atributos de classe.

Eu acho muito legal que praticamente todo o sistema OO do Python possa ser expresso em Python. :)

Ah, e eu escrevi um post no blog sobre descritores há algum tempo, se você estiver interessado.

Eevee
fonte
35
Não há necessidade de adicionar o método add_property. setattr (Foo, 'name', property (func))
Courtney D
8
Seu "Mas esse é o problema ..." me salvou várias horas de trabalho. Obrigado.
Matt Howell
2
Se você deseja definir uma propriedade em uma única instância, é possível criar uma classe no tempo de execução e modificar __class__ .
Wilfred Hughes
1
e quanto a @ myproperty.setter? Como adicioná-lo dinamicamente?
LRMAAX
Você não precisa adicionar uma propriedade a um objeto inicializado. Fazer isso pode significar que ele adere apenas à instância, mas eu teria que verificar isso duas vezes. Eu sei que me deparei com um problema semelhante em que minhas propriedades dinâmicas eram apenas instâncias. Também terminei com uma configuração estática e a que eu queria que fosse para o objeto, para que futuras Inicializações as usassem. Minha postagem está abaixo e cria funções auxiliares e maneiras fáceis de acessar tudo facilmente. .x para a propriedade, ._x para os dados brutos que o getter / setter usa (que pode ser Nenhum) e .__ x para o objeto acessador.
Acecool 27/01
57

O objetivo é criar uma classe simulada que se comporte como um conjunto de resultados db.

Então, o que você quer é um dicionário onde você possa soletrar a ['b'] como ab?

Isso é fácil:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__
bobince
fonte
1
em uma configuração mais geral, isso serve a propósitos limitados. se o ditado tiver hierarquia multinível, como d = {'a1': {'b': 'c'}, 'a2': ...}, enquanto você pode executar d.a1 ou d.a2, você pode ' t fazer d.a1.b
Shreyas
1
Uma coisa a ter em mente é que este permite definir valores de atributo para atributos com o mesmo nome como métodos de dicionários ou atributos, mas não permite recuperar os valores da mesma forma novamente: d.items = 1, d.itemsretornos <built-in method items of atdict object at ...>. Você ainda pode fazer d["items"]ou usar em __getattribute__vez de __getattr__, mas isso evita o uso da maioria dos métodos do dict.
Marcono1234
Basta usar a biblioteca munch ! (Garfo do cacho) #
1955 Brian Peterson
38

Parece que você poderia resolver esse problema de maneira muito mais simples com a namedtuple, uma vez que você conhece toda a lista de campos com antecedência.

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

Se você absolutamente precisar escrever seu próprio setter, precisará fazer a metaprogramação no nível da classe; property()não funciona em instâncias.

Eevee
fonte
Boa ideia. Infelizmente, estou preso com o python 2.4 no momento.
Anthony Kong
2
O cara que escreveu namedtuplemerece um prêmio por torná-lo suave e elegante para ser fiel aos princípios orientados a objetos.
perfil completo de Keith Pinson
4
Desculpe, na melhor das hipóteses, essa resposta é aplicável apenas ao caso especial em que uma classe desejada é composta apenas por atributos somente leitura, todos os que conhecem previamente. Em outras palavras, não acho que ele lide com a questão mais ampla de como adicionar propriedades gerais - não apenas somente leitura - a uma classe em tempo de execução (nem a versão atual da outra resposta adicional também publicado pelo autor).
11133 martineau
@martineau então ... passar mais argumentos para property()? não há nada nas respostas que seja específico para propriedades somente leitura.
Eevee
32

Você não precisa usar uma propriedade para isso. Basta substituir __setattr__para torná-los somente leitura.

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

Tada.

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
Ryan
fonte
9

Como adicionar propriedade a uma classe python dinamicamente?

Digamos que você tenha um objeto ao qual deseja adicionar uma propriedade. Normalmente, quero usar propriedades quando preciso começar a gerenciar o acesso a um atributo no código que possui uso downstream, para que eu possa manter uma API consistente. Agora, normalmente os adicionamos ao código-fonte em que o objeto está definido, mas vamos supor que você não tenha esse acesso, ou você precisará realmente escolher dinamicamente suas funções programaticamente.

Crie uma turma

Usando um exemplo baseado na documentação paraproperty , vamos criar uma classe de objeto com um atributo "oculto" e criar uma instância dele:

class C(object):
    '''basic class'''
    _x = None

o = C()

Em Python, esperamos que haja uma maneira óbvia de fazer as coisas. No entanto, neste caso, vou mostrar duas maneiras: com notação decoradora e sem. Primeiro, sem a notação do decorador. Isso pode ser mais útil para a atribuição dinâmica de getters, setters ou deleters.

Dinâmico (também conhecido como Monkey Patching)

Vamos criar alguns para a nossa classe:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

E agora os atribuímos à propriedade. Observe que poderíamos escolher nossas funções programaticamente aqui, respondendo à pergunta dinâmica:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

E uso:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

Decoradores

Poderíamos fazer o mesmo que fizemos acima com a notação do decorador, mas nesse caso, devemos nomear os métodos com o mesmo nome (e eu recomendo mantê-lo igual ao atributo), para que a atribuição programática não seja tão trivial quanto está usando o método acima:

@property
def x(self):
    '''I'm the 'x' property.'''
    return self._x

@x.setter
def x(self, value):
    self._x = value

@x.deleter
def x(self):
    del self._x

E atribua o objeto de propriedade com seus setters e deleters provisionados à classe:

C.x = x

E uso:

>>> help(C.x)
Help on property:

    I'm the 'x' property.

>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
Aaron Hall
fonte
5

Fiz uma pergunta semelhante neste post do Stack Overflow para criar uma fábrica de classes que criou tipos simples. O resultado foi essa resposta que possuía uma versão funcional da fábrica de classes. Aqui está um trecho da resposta:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

Você pode usar alguma variação disso para criar valores padrão, que é seu objetivo (também há uma resposta nessa pergunta que lida com isso).

kjfletch
fonte
4

Não tenho certeza se eu entendo completamente a pergunta, mas você pode modificar as propriedades da instância em tempo de execução com o built-in __dict__da sua classe:

class C(object):
    def __init__(self, ks, vs):
        self.__dict__ = dict(zip(ks, vs))


if __name__ == "__main__":
    ks = ['ab', 'cd']
    vs = [12, 34]
    c = C(ks, vs)
    print(c.ab) # 12
Crescent Fresh
fonte
Em essência, minha pergunta é descobrir se é possível criar uma nova propriedade em tempo de execução. O consenso parece ser negativo. Sua sugestão é certamente simples e prática. (O mesmo para outras respostas que usa dict )
Anthony Kong
Uma resposta simples também seria:self.__dict__[key] = value
Allan Karlson
4

Para quem vem dos mecanismos de pesquisa, eis as duas coisas que eu procurava ao falar sobre propriedades dinâmicas :

class Foo:
    def __init__(self):
        # we can dynamically have access to the properties dict using __dict__
        self.__dict__['foo'] = 'bar'

assert Foo().foo == 'bar'


# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
    def __init__(self):
        self._data = {}
    def __getattr__(self, key):
        return self._data[key]
    def __setattr__(self, key, value):
        self._data[key] = value

bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'

__dict__ é bom se você deseja colocar propriedades criadas dinamicamente. __getattr__é bom fazer algo apenas quando o valor é necessário, como consultar um banco de dados. A combinação set / get é boa para simplificar o acesso aos dados armazenados na classe (como no exemplo acima).

Se você deseja apenas uma propriedade dinâmica, consulte a função interna property () .

tleb
fonte
4

Você não pode adicionar um novo property()a uma instância em tempo de execução, porque propriedades são descritores de dados. Em vez disso, você deve criar dinamicamente uma nova classe ou sobrecarga __getattribute__para processar descritores de dados nas instâncias.

Alex Gaynor
fonte
Isto está errado. Você pode adicionar a propriedade à classe e acessá-la no método
Ahmed
2

A melhor maneira de conseguir é definindo __slots__. Dessa forma, suas instâncias não podem ter novos atributos.

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

Que imprime 12

    c.ab = 33

Isso dá: AttributeError: 'C' object has no attribute 'ab'

nosklo
fonte
2

Apenas outro exemplo de como obter o efeito desejado

class Foo(object):

    _bar = None

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    def __init__(self, dyn_property_name):
        setattr(Foo, dyn_property_name, Foo.bar)

Então agora podemos fazer coisas como:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5
Lehins
fonte
2

Aqui está uma solução que:

  • Permite especificar nomes de propriedades como seqüências de caracteres , para que possam vir de alguma fonte de dados externa, em vez de todas serem listadas no seu programa.
  • Adiciona as propriedades quando a classe é definida , em vez de toda vez que um objeto é criado.

Após a definição da classe, basta fazer isso para adicionar uma propriedade a ela dinamicamente:

setattr(SomeClass, 'propertyName', property(getter, setter))

Aqui está um exemplo completo, testado em Python 3:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)
David Grayson
fonte
1

Você pode usar o seguinte código para atualizar atributos de classe usando um objeto de dicionário:

class ExampleClass():
    def __init__(self, argv):
        for key, val in argv.items():
            self.__dict__[key] = val

if __name__ == '__main__':
    argv = {'intro': 'Hello World!'}
    instance = ExampleClass(argv)
    print instance.intro
Anthony Holloman
fonte
1

Isso é um pouco diferente do que o OP queria, mas agitei meu cérebro até encontrar uma solução funcional, então estou colocando aqui para o próximo cara / garota

Eu precisava de uma maneira de especificar setters e getters dinâmicos.

class X:
    def __init__(self, a=0, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def _make_properties(cls, field_name, inc):
        _inc = inc

        def _get_properties(self):
            if not hasattr(self, '_%s_inc' % field_name):
                setattr(self, '_%s_inc' % field_name, _inc)
                inc = _inc
            else:
                inc = getattr(self, '_%s_inc' % field_name)

            return getattr(self, field_name) + inc

        def _set_properties(self, value):
            setattr(self, '_%s_inc' % field_name, value)

        return property(_get_properties, _set_properties)

Conheço meus campos com antecedência, por isso vou criar minhas propriedades. NOTA: você não pode fazer isso por instância, essas propriedades existirão na classe !!!

for inc, field in enumerate(['a', 'b', 'c']):
    setattr(X, '%s_summed' % field, X._make_properties(field, inc))

Vamos testar tudo agora ..

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0

assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2

# we set the variables to something
x.a = 1
x.b = 2
x.c = 3

assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5

# we're changing the inc now
x.a_summed = 1 
x.b_summed = 3 
x.c_summed = 5

assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

Isso é confuso? Sim, desculpe, não pude apresentar exemplos significativos do mundo real. Além disso, isso não é para os mais claros.

Javier Buzzi
fonte
Se bem me lembro, encontrei uma maneira, durante todos os meus testes, de criar uma propriedade do tipo STATIC / adicionado dinamicamente g / setter. Eu teria que passar por todas as minhas anteriores - mas ser capaz de adicionar algo que é compartilhado entre todas as instâncias é definitivamente possível. Quanto à criação de um processo por instância ... Tenho certeza que você pode, para que uma instância tenha algo que a outra não. Eu teria que verificar, mas me deparei com algo assim também (nas minhas primeiras tentativas cometi um erro que fazia com que as funções fossem criadas, mas nem todas as instâncias as tiveram por causa de uma falha)
Acecool
Além disso, todas as soluções possíveis são bem-vindas, pois esse é um repositório de conhecimento. Também é empolgante ver as diferentes maneiras pelas quais as pessoas criam soluções para um problema. Minha solução é MUITO, você reduziu para algo mais simples de compartilhar. Também fiz uma variante menor - deve estar em algum lugar deste tópico - e acabei de perceber que não é a que
publiquei
0

Isso parece funcionar (mas veja abaixo):

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
        self.__dict__.update(self)
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

Se você precisar de um comportamento mais complexo, fique à vontade para editar sua resposta.

editar

O seguinte provavelmente seria mais eficiente em termos de memória para grandes conjuntos de dados:

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
    def __getattr__(self,name):
        return self[name]
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
David X
fonte
0

Para responder ao principal impulso de sua pergunta, você deseja um atributo somente leitura de um ditado como uma fonte de dados imutável:

O objetivo é criar uma classe simulada que se comporte como um conjunto de resultados db.

Por exemplo, se uma consulta ao banco de dados retornar, usando uma expressão dict {'ab':100, 'cd':200}, então eu veria

>>> dummy.ab
100

Vou demonstrar como usar a namedtuplepartir do collectionsmódulo para fazer exatamente isso:

import collections

data = {'ab':100, 'cd':200}

def maketuple(d):
    '''given a dict, return a namedtuple'''
    Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
    return Tup(**d)

dummy = maketuple(data)
dummy.ab

retorna 100

Aaron Hall
fonte
0
class atdict(dict):
  def __init__(self, value, **kwargs):
    super().__init__(**kwargs)
    self.__dict = value

  def __getattr__(self, name):
    for key in self.__dict:
      if type(self.__dict[key]) is list:
        for idx, item in enumerate(self.__dict[key]):
          if type(item) is dict:
            self.__dict[key][idx] = atdict(item)
      if type(self.__dict[key]) is dict:
        self.__dict[key] = atdict(self.__dict[key])
    return self.__dict[name]



d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})

print(d1.a.b[0].c)

E a saída é:

>> 1
Serhii Khachko
fonte
0

Estendendo a idéia do kjfletch

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.

def Struct(*args, **kwargs):
    FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                 types.FunctionType, types.MethodType)
    def init(self, *iargs, **ikwargs):
        """Asume that unamed args are placed in the same order than
        astuple() yields (currently alphabetic order)
        """
        kw = list(self.__slots__)

        # set the unnamed args
        for i in range(len(iargs)):
            k = kw.pop(0)
            setattr(self, k, iargs[i])

        # set the named args
        for k, v in ikwargs.items():
            setattr(self, k, v)
            kw.remove(k)

        # set default values
        for k in kw:
            v = kwargs[k]
            if isinstance(v, FUNCTIONS):
                v = v()
            setattr(self, k, v)

    def astuple(self):
        return tuple([getattr(self, k) for k in self.__slots__])

    def __str__(self):
        data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
        return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))

    def __repr__(self):
        return str(self)

    def __eq__(self, other):
        return self.astuple() == other.astuple()

    name = kwargs.pop("__name__", "MyStruct")
    slots = list(args)
    slots.extend(kwargs.keys())
    # set non-specific default values to None
    kwargs.update(dict((k, None) for k in args))

    return type(name, (object,), {
        '__init__': init,
        '__slots__': tuple(slots),
        'astuple': astuple,
        '__str__': __str__,
        '__repr__': __repr__,
        '__eq__': __eq__,
    })


Event = Struct('user', 'cmd', \
               'arg1', 'arg2',  \
               date=time.time, \
               __name__='Event')

aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()

bb = Event(*raw)
print(bb)

if aa == bb:
    print('Are equals')

cc = Event(cmd='foo')
print(cc)

Resultado:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>
asterio gonzalez
fonte
0

Embora muitas respostas sejam dadas, não consegui encontrar uma com a qual eu esteja feliz. Eu descobri minha própria solução, que faz o propertytrabalho para o caso dinâmico. A fonte para responder à pergunta original:

#!/usr/local/bin/python3

INITS = { 'ab': 100, 'cd': 200 }

class DP(dict):
  def __init__(self):
    super().__init__()
    for k,v in INITS.items():
        self[k] = v 

def _dict_set(dp, key, value):
  dp[key] = value

for item in INITS.keys():
  setattr(
    DP,
    item,
    lambda key: property(
      lambda self: self[key], lambda self, value: _dict_set(self, key, value)
    )(item)
  )

a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False
Rob L
fonte
0

Algo que funciona para mim é o seguinte:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x

    x=property(g,s,d)


c = C()
c.x="a"
print(c.x)

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

Resultado

a
aa
prosti
fonte
-1

Recentemente, eu tive um problema semelhante, a solução que eu criei usa __getattr__e, __setattr__para as propriedades que eu quero que ele lide, todo o resto é passado para os originais.

class C(object):
    def __init__(self, properties):
        self.existing = "Still Here"
        self.properties = properties

    def __getattr__(self, name):
        if "properties" in self.__dict__ and name in self.properties:
            return self.properties[name] # Or call a function, etc
        return self.__dict__[name]

    def __setattr__(self, name, value):
        if "properties" in self.__dict__ and name in self.properties:
            self.properties[name] = value
        else:
            self.__dict__[name] = value

if __name__ == "__main__":
    my_properties = {'a':1, 'b':2, 'c':3}
    c = C(my_properties)
    assert c.a == 1
    assert c.existing == "Still Here"
    c.b = 10
    assert c.properties['b'] == 10
teeks99
fonte
Eu olhei para isso, no entanto, você está tecnicamente passando por uma lista em seus ajudantes getter e setter. Por esse motivo, cada chamada seria mais lenta porque você a procuraria em uma lista, primeiro, em vez de acessá-la diretamente. A menos que o Python mapeie automaticamente para você; pode ser, mas ainda não fiz o benchmarking para ter certeza, mas é uma preocupação para mim antes de tentar. Segundo, ao fazer isso, você precisa definir os auxiliares de outra maneira. Você também não pode bloquear tipos de dados e / ou valores sem terminar com um dicionário grande ou muitas linhas extras.
Acecool 27/01
ou seja: eu precisaria criar uma classe base da qual estendesse todos os meus filhos que usam o sistema, OU eu teria que adicionar funções mágicas s / getattr a tudo e duplicar o sistema todas as vezes. A declaração de propriedades também significa que você deve configurá-las de uma maneira e, se desejar algum suporte adicional, como eu listei, como proteção de tipo e / ou de dados para permitir ou impedir a atribuição de dados e outros auxiliares , é necessário codificá-los. Concedido, você pode tornar o sistema semelhante em comportamento, mas ele acaba sendo declarado de maneira um pouco diferente e mais volumosa.
Acecool
-1

Aqui está o exemplo simples para criar objeto de propriedade programaticamente.

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''
Suhas Siddu
fonte
-2

A única maneira de anexar dinamicamente uma propriedade é criar uma nova classe e sua instância com sua nova propriedade.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
Utku ALTINKAYA
fonte
1
isso não parece funcionar. atribuirá o resultado da propriedade, não a própria propriedade.
mjallday
Isto está incorreto. Anexo propriedades dinâmicas ao meu sistema sem precisar inicializar a classe. Inicialize sendo x = Example () e adicione a propriedade a x.
Acecool 27/01
Se você olhar para o meu código, verá que eu uso a classe ExampleBase: pass e, em seguida, a classe Example (ExampleBase): ... então anexo as propriedades ao ExampleBase, porque o nome existe e, desde que o Example se estende a ele, tem acesso a tudo. Uso o __ var para que o auxiliar do acessador possa ter acesso direto aos objetos do acessador, uso _ para os dados armazenados (brutos) que podem ser Nenhum e nenhum sublinhado para a propriedade real que passa pelo getter. Posso chamar a função getter usando uma função adicionada dinamicamente ou usar a propriedade Tudo sem ter iniciado primeiro.
Acecool 27/01
Nota: Eu mencionei - mas minha definição para definição significa que a referência existe no espaço para nome - ou seja: class Exemplo (Object): pass ... Existe, mas não foi inicializado. Inicialização significa blá = Exemplo (); agora o objeto foi 'duplicado' e construído, depois armazenado como uma referência em blá. --- Se você fizer isso, as funções / propriedades adicionadas dinamicamente devem estar apenas na instância - o problema que tive com isso foi mesmo se as funções existissem, houve casos em que recebi um erro dizendo que não. O erro de bloqueio interrompeu a criação ou a execução assíncrona.
Acecool 27/01
Algumas outras notas: É possível criar propriedades dinamicamente e funcionar de uma maneira que exista apenas por instância. Você também pode torná-los para que eles existam para o objeto (que é o que você deseja na maioria dos casos). E há casos em que os elementos adicionados são 'estáticos', ou seja, as mesmas referências e os valores retornados são compartilhados em todas as instâncias - se você atualizar em uma área, todos ficam iguais.
Acecool
-6

Muitas das respostas fornecidas requerem tantas linhas por propriedade, ou seja, / e / ou - o que eu consideraria uma implementação feia ou tediosa por causa da repetitividade necessária para várias propriedades, etc. Eu prefiro manter as coisas em ordem / simplificar até que elas não pode mais ser simplificado ou até que não sirva muito para isso.

Resumindo: em trabalhos concluídos, se eu repetir duas linhas de código, normalmente a converto em uma função auxiliar de linha única, e assim por diante ... Simplifico argumentos matemáticos ou ímpares como (start_x, start_y, end_x, end_y) para (x, y, w, h) ou seja, x, y, x + w, y + h (às vezes requer min / max ou se w / h são negativos e a implementação não gosta disso, vou subtrair de x / y e abs p / h, etc.).

Substituir os getters / setters internos é um bom caminho a percorrer, mas o problema é que você precisa fazer isso para todas as classes ou orientar a classe nessa base ... Isso não funciona para mim, como eu preferiria ser livre para escolher os filhos / pais por herança, nós filhos, etc.

Eu criei uma solução que responde à pergunta sem usar um tipo de dados Dict para fornecer os dados, pois acho que é tedioso para inserir os dados, etc.

Minha solução exige que você adicione 2 linhas extras acima da sua classe para criar uma classe base para a classe na qual você deseja adicionar as propriedades e, em seguida, 1 linha por e você tem a opção de adicionar retornos de chamada para controlar os dados e informá-lo quando os dados forem alterados , restrinja os dados que podem ser definidos com base no valor e / ou tipo de dados e muito mais.

Você também tem a opção de usar _object.x, _object.x = value, _object.GetX (), _object.SetX (value) e eles são tratados de forma equivalente.

Além disso, os valores são os únicos dados não estáticos atribuídos à instância da classe, mas a propriedade real é atribuída à classe, significando o que você não deseja repetir, não precisa ser repetido ... Você pode atribuir um valor padrão para que o getter não precise dele todas as vezes, embora exista uma opção para substituir o valor padrão padrão e outra opção para que o getter retorne o valor bruto armazenado, substituindo os retornos padrão (observação: este método significa que o valor bruto é atribuído apenas quando um valor é atribuído; caso contrário, é Nenhum - quando o valor é Redefinido, ele atribui Nenhum, etc.)

Também existem muitas funções auxiliares - a primeira propriedade adicionada adiciona mais ou menos 2 auxiliares à classe para referenciar os valores da instância ... Eles são ResetAccessors (_key, ..) varargs repetidos (todos podem ser repetidos usando o primeiro argumento args ) e SetAccessors (_key, _value) com a opção de serem adicionados mais à classe principal para auxiliar na eficiência - os planejados são: uma maneira de agrupar os acessadores, portanto, se você tende a redefinir alguns de cada vez, sempre , você pode atribuí-los a um grupo e redefinir o grupo em vez de repetir as teclas nomeadas a cada vez e muito mais.

O valor armazenado da instância / bruto é armazenado na classe , a classe. referencia a classe Accessor que contém vars / valores / funções estáticos para a propriedade _classe. é a própria propriedade que é chamada quando acessada através da classe de instância durante a configuração / obtenção, etc.

O Accessor _class .__ aponta para a classe, mas como é interno, ele precisa ser atribuído à classe, e foi por isso que eu optei por usar __Name = AccessorFunc (...) para atribuí-la, uma única linha por propriedade com muitas opções opcionais. argumentos a serem usados ​​(usando varargs com chave porque são mais fáceis e mais eficientes de identificar e manter) ...

Também crio muitas funções, como mencionado, algumas das quais usam informações da função de acessador para que não precisem ser chamadas (pois é um pouco inconveniente no momento - agora você precisa usar _class. .FunctionName (_class_instance , args) - Eu usei a pilha / rastreamento para capturar a referência da instância para capturar o valor adicionando as funções que executam essa maratona de bits ou adicionando os acessadores ao objeto e usando self (com o nome this para indicar que eles é para a instância e retém o acesso a si próprio, a referência da classe AccessorFunc e outras informações nas definições de função).

Não está bem feito, mas é um apoio fantástico para os pés. Nota: Se você não usar __Name = AccessorFunc (...) para criar as propriedades, não terá acesso à tecla __, mesmo que eu a defina na função init. Se o fizer, não haverá problemas.

Além disso: Observe que Nome e Chave são diferentes ... O nome é 'formal', usado na Criação de Nome da Função, e a chave é para armazenamento e acesso a dados. ie _class.x onde x minúsculo é a chave, o nome seria X maiúsculo para que GetX () seja a função em vez de Getx () que parece um pouco estranha. isso permite que o self.x funcione e pareça apropriado, mas também permite GetX () e pareça apropriado.

Eu tenho uma classe de exemplo configurada com a chave / nome idêntico e diferente para mostrar. várias funções auxiliares criadas para gerar os dados (Nota: nem tudo isso está completo) para que você possa ver o que está acontecendo.

A lista atual de funções usando a tecla: x, nome: X gera como:

Esta não é de forma alguma uma lista abrangente - existem algumas que ainda não foram publicadas no momento da publicação ...

_instance.SetAccessors( _key, _value [ , _key, _value ] .. )                   Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines.    In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. )                                 Instance Class Helper Function: Allows resetting many key stored values to None on a single line.                                           In short: Calls this.Reset<Name>() for each name provided.


Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.

this.GetX( _default_override = None, _ignore_defaults = False )                 GET:            Returns    IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None  .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE       100
this.GetXRaw( )                                                                 RAW:            Returns    STORED_VALUE                                                                                                     100
this.IsXSet( )                                                                  ISSET:          Returns    ( STORED_VALUE != None )                                                                                         True

this.GetXToString( )                                                            GETSTR:         Returns    str( GET )                                                                                                       100
this.GetXLen( _default_override = None, _ignore_defaults = False )              LEN:            Returns    len( GET )                                                                                                       3
this.GetXLenToString( _default_override = None, _ignore_defaults = False )      LENSTR:         Returns    str( len( GET ) )                                                                                                3
this.GetXDefaultValue( )                                                        DEFAULT:        Returns    DEFAULT_VALUE                                                                                                    1111

this.GetXAccessor( )                                                            ACCESSOR:       Returns    ACCESSOR_REF ( self.__<key> )                                                                                    [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848        Default: 1111       Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}     Allowed Values: None
this.GetXAllowedTypes( )                                                        ALLOWED_TYPES:  Returns    Allowed Data-Types                                                                                               {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( )                                                       ALLOWED_VALUES: Returns    Allowed Values                                                                                                   None

this.GetXHelpers( )                                                             HELPERS:        Returns    Helper Functions String List - ie what you're reading now...                                                     THESE ROWS OF TEXT
this.GetXKeyOutput( )                                                           Returns information about this Name / Key                                                                                                   ROWS OF TEXT
this.GetXGetterOutput( )                                                        Returns information about this Name / Key                                                                                                   ROWS OF TEXT

this.SetX( _value )                                                             SET:            STORED_VALUE Setter - ie Redirect to __<Key>.Set                                                                            N / A
this.ResetX( )                                                                  RESET:          Resets STORED_VALUE to None                                                                                                 N / A

this.HasXGetterPrefix( )                                                        Returns Whether or Not this key has a Getter Prefix...                                                                                      True
this.GetXGetterPrefix( )                                                        Returns Getter Prefix...                                                                                                                    Get

this.GetXName( )                                                                Returns Accessor Name - Typically Formal / Title-Case                                                                                       X
this.GetXKey( )                                                                 Returns Accessor Property Key - Typically Lower-Case                                                                                        x
this.GetXAccessorKey( )                                                         Returns Accessor Key - This is to access internal functions, and static data...                                                             __x
this.GetXDataKey( )                                                             Returns Accessor Data-Storage Key - This is the location where the class instance value is stored..                                         _x

Alguns dos dados que estão sendo produzidos são:

Isto é para uma nova classe criada usando a classe Demo sem nenhum dado atribuído além do nome (para que possa ser gerado), que é _foo, o nome da variável que eu usei ...

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        1111                | _x:       None                     | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        2222                | _y:       None                     | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        3333                | _z:       None                     | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     <class 'int'>       | _Blah:    None                     | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    1                   | _Width:   None                     | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   0                   | _Height:  None                     | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    2                   | _Depth:   None                     | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |


this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       False     this.GetX( ):        1111                     this.GetXRaw( ):       None                     this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       4    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       False     this.GetY( ):        2222                     this.GetYRaw( ):       None                     this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       4    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       False     this.GetZ( ):        3333                     this.GetZRaw( ):       None                     this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       4    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    False     this.GetBlah( ):     <class 'int'>            this.GetBlahRaw( ):    None                     this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    13   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   False     this.GetWidth( ):    1                        this.GetWidthRaw( ):   None                     this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   1    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   False     this.GetDepth( ):    2                        this.GetDepthRaw( ):   None                     this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  False     this.GetHeight( ):   0                        this.GetHeightRaw( ):  None                     this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

E isso é depois de atribuir todas as propriedades _foo (exceto o nome) os seguintes valores na mesma ordem: 'string', 1.0, True, 9, 10, False

this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       True      this.GetX( ):        10                       this.GetXRaw( ):       10                       this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       2    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       True      this.GetY( ):        10                       this.GetYRaw( ):       10                       this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       2    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       True      this.GetZ( ):        10                       this.GetZRaw( ):       10                       this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       2    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    True      this.GetBlah( ):     string Blah              this.GetBlahRaw( ):    string Blah              this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    11   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   True      this.GetWidth( ):    False                    this.GetWidthRaw( ):   False                    this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   5    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   True      this.GetDepth( ):    9                        this.GetDepthRaw( ):   9                        this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  True      this.GetHeight( ):   9                        this.GetHeightRaw( ):  9                        this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        10                  | _x:       10                       | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        10                  | _y:       10                       | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        10                  | _z:       10                       | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     string Blah         | _Blah:    string Blah              | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    False               | _Width:   False                    | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   9                   | _Height:  9                        | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    9                   | _Depth:   9                        | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |

Observe que, devido a tipos de dados restritos ou restrições de valor, alguns dados não foram atribuídos - isso ocorre por design. O setter proíbe que tipos de dados ou valores incorretos sejam atribuídos, mesmo que sejam atribuídos como um valor padrão (a menos que você substitua o comportamento de proteção de valor padrão)

O código não foi publicado aqui porque não havia espaço após os exemplos e explicações ... Também porque ele mudará.

Observação: no momento desta postagem, o arquivo está confuso - isso será alterado. Mas, se você executá-lo em Sublime Text e compilá-lo ou em Python, ele compilará e cuspirá uma tonelada de informações - a parte do AccessorDB não está concluída (que será usada para atualizar os auxiliares Print Getters e GetKeyOutput funções, além de serem alteradas para uma função de Instância, provavelmente inseridas em uma única função e renomeadas - procure-a ..)

Próximo: Nem tudo é necessário para que ele seja executado - muitas das coisas comentadas na parte inferior são para mais informações usadas para depuração - pode não estar presente quando você o baixa. Se for, você poderá descomentar e recompilar para obter mais informações.

Estou procurando uma solução alternativa para a necessidade de MyClassBase: pass, MyClass (MyClassBase): ... - se você souber de uma solução - poste-a.

A única coisa necessária na classe são as linhas __ - o str é para depuração e o init - elas podem ser removidas da classe Demo, mas você precisará comentar ou remover algumas das linhas abaixo (_foo / 2/3 ) ..

As classes String, Dict e Util na parte superior fazem parte da minha biblioteca Python - elas não estão completas. Copiei algumas coisas que precisava da biblioteca e criei algumas novas. O código completo será vinculado à biblioteca completa e o incluirá, além de fornecer chamadas atualizadas e remover o código (na verdade, o único código restante será a Classe Demo e as instruções de impressão - o sistema AccessorFunc será movido para a biblioteca). ..

Parte do arquivo:

##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
    pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
    __Name      = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name',      default = 'AccessorFuncDemoClass',  allowed_types = ( TYPE_STRING ),                    allowed_values = VALUE_ANY,                 documentation = 'Name Docs',        getter_prefix = 'Get',  key = 'Name',       allow_erroneous_default = False,    options = { } )
    __x         = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X',         default = 1111,                     allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ),       allowed_values = VALUE_ANY,                 documentation = 'X Docs',           getter_prefix = 'Get',  key = 'x',          allow_erroneous_default = False,    options = { } )
    __Height    = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height',    default = 0,                        allowed_types = TYPE_INTEGER,                       allowed_values = VALUE_SINGLE_DIGITS,       documentation = 'Height Docs',      getter_prefix = 'Get',  key = 'Height',     allow_erroneous_default = False,    options = { } )

Essa beleza facilita incrivelmente a criação de novas classes com propriedades adicionadas dinamicamente com AccessorFuncs / callbacks / data-type / value enforcement, etc.

Por enquanto, o link está em (Este link deve refletir as alterações no documento.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

Além disso: se você não usar o Sublime Text, recomendo-o no Notepad ++, Atom, Visual Code e outros por causa de implementações de encadeamento apropriadas que o tornam muito, muito mais rápido de usar ... Também estou trabalhando em um código semelhante ao IDE sistema de mapeamento para ele - consulte: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Adicione o repositório no Package Manager primeiro e depois instale o plug-in - quando a versão 1.0.0 estiver pronta, adicionarei para a lista principal de plugins ...)

Espero que esta solução ajude ... e, como sempre:

Só porque funciona, não dá certo - Josh 'Acecool' Moser

Acecool
fonte
Eu queria acrescentar uma exibição rápida do que a classe parecia que você não precisa abrir o arquivo de código mas os comentários não aparecem para apoiá-lo ..
Acecool
Aparentemente, isso está recebendo muito ódio, o que é confuso. Ele faz exatamente o que o OP está solicitando - adicionando propriedades dinamicamente a um objeto. Ele também adiciona funções auxiliares, que não precisam ser incluídas - talvez seja por isso que está ficando odioso - e também garante que o desenvolvedor tenha uma maneira fácil de acessar a propriedade (.x) que é processada através do getter, o valor bruto armazenado (._x) que pode ser Nenhum quando .x retorna o padrão ou outra coisa, e uma maneira de acessar o acessador para usar ajudantes, mudar coisas, etc. (.__ x) ....
Acecool