Como adicionar propriedade a uma classe dinamicamente?
o objectivo é criar uma classe mock que se comporte como um conjunto de resultados db.
por exemplo, se uma pesquisa de base de dados retorna, usando uma expressão dict, {'ab':100, 'cd':200}
, então eu gostaria de ver:
>>> dummy.ab
100
No início, 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.ab
devolve um objecto de propriedade.
substituir a Linha setattr
por k = property(lambda x: vs[i])
não tem qualquer utilidade.
__getattribute__
?
18 answers
{Você[26]}pode adicionar uma propriedade a uma classe dinamicamente. Mas esse é o truque: você tem que 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 a descritor. É um objeto que fornece tratamento personalizado para um dado atributo, em uma dada classe . Como uma maneira para considerar uma enorme árvore de if
.
Quando pergunto por foo.b
no exemplo acima, o Python vê que o b
definido na classe implementa o protocolodescritor -O que significa que é um objecto com um __get__
, __set__
, ou método de __delete__
O descritor assume a responsabilidade de lidar com esse atributo, de modo que o Python chama Foo.b.__get__(foo, Foo)
, e o valor de retorno é repassado para você como o valor do atributo. No caso de property
, cada um destes métodos chama apenas de fget
, fset
, ou fdel
passaste para o construtor.
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 humble é apenas mais um tipo de descritor. Seu __get__
tackles sobre a instância chamadora como o primeiro argumento; em efeito, ele faz isso:
def __get__(self, instance, owner):
return functools.partial(self.function, instance)
De qualquer forma, suspeito que é por isso que os descritores só trabalham nas aulas: são um formalização do material que alimenta classes em primeiro lugar. Eles são mesmo a exceção à regra: você pode obviamente atribuir descritores a uma classe, e as classes são exemplos de type
! Na verdade, tentar ler Foo.b
ainda chama property.__get__
; é apenas idiomático para os descritores se retornarem quando acessados como atributos de classe.
Acho muito fixe que praticamente todo o sistema OO do Python possa ser expresso em Python. :)
E eu escrevi um wordy blog post sobre descritores um tempo atrás, se você estiver interessado.
Então o que você quer é um dicionário onde você pode soletrar a ['b'] como A. b? Isso é fácil.O objectivo é criar uma classe mock que se comporte como um conjunto de resultados db.
class atdict(dict):
__getattr__= dict.__getitem__
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
namedtuple
, já que você conhece toda a lista de campos antes do tempo.
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ê precisa absolutamente de escrever o seu próprio setter, você terá que fazer o metaprogramação no nível de classe; property()
não funciona em instâncias.
__setattr__
para os Fazer ler apenas.
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!
Você não pode adicionar um novo property()
a uma instância no tempo de execução, porque as propriedades são descritores de dados. Em vez disso, você deve criar dinamicamente uma nova classe, ou sobrecarga __getattribute__
, a fim de processar descritores de dados em instâncias.
Eu fiz uma pergunta semelhante nesta pilha Overflow post para criar uma fábrica de classes que criou tipos simples. O resultado foi esta resposta que tinha uma versão de trabalho da fábrica de classe. 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ê poderia usar alguma variação disto para criar valores padrão que é o seu objetivo (há também uma resposta nessa pergunta que lida com isso).
Não tenho a certeza se 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
Como adicionar propriedades a uma classe python dinamicamente?Diz que tens um objecto ao qual queres adicionar uma propriedade. Normalmente, eu quero usar propriedades quando eu preciso começar a gerenciar o acesso a um atributo em código que tem uso downstream, para que eu possa manter uma API consistente. Agora eu vou normalmente adicioná-los ao código fonte onde o objeto é definido, mas vamos assumir que você não tem esse acesso, ou você precisa realmente dinamicamente escolher suas funções programaticamente.
Criar uma classe
Usando um exemplo baseado na documentação para property
, vamos criar uma classe de objeto com um atributo" escondido " e criar uma instância dele:
class C(object):
'''basic class'''
_x = None
o = C()
Em Python, esperamos que haja uma forma óbvia de fazer as coisas. No entanto, neste caso, vou mostrar duas maneiras: com a notação decoradora, e sem. Primeiro, sem a notação de decorador. Isto pode ser mais útil para a atribuição dinâmica de getters, setters, ou apagadores.
Dynamic (t. c. p.Monkey Patching)
Vamos criar alguns para a nossa turma.def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
E agora atribuímos isto à propriedade. Note que nós poderíamos escolher nossas funções programaticamente aqui, respondendo à pergunta dinâmica:
C.x = property(getx, setx, delx, "I'm the 'x' property.")
E utilização:
>>> 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 decoradora, mas neste caso, devemosnomear os métodos todos com o mesmo nome (e eu recomendaria mantê-lo the same as the attribute), so programmatic assignment is not so trivial as it is using the above method:
@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 atribuir o objecto da propriedade com os seus conjuntos provisionados e apagadores para a classe:
C.x = x
E utilização:
>>> 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
A melhor maneira de conseguir é definindo __slots__
. Assim, as tuas 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
Isso imprime 12
c.ab = 33
Isso dá: AttributeError: 'C' object has no attribute 'ab'
Apenas mais um 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
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ê quiser colocar propriedades criadas dinamicamente. __getattr__
é bom apenas fazer algo quando o valor é necessário, como consultar um banco de dados. O conjunto / get combo é bom para simplificar o acesso aos dados armazenados na classe (como no exemplo acima).
Isto 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 precisar de um comportamento mais complexo, esteja à vontade para editar a sua resposta.
Editar
O seguinte seria provavelmente 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__)
Para responder ao impulso principal da sua pergunta, você quer um atributo só de leitura de um dict como uma fonte de dados imutável:
O objectivo é criar uma classe mock que se comporte como um conjunto de resultados db.
Por exemplo, se uma pesquisa de base de dados retornar, usando uma expressão dict,
{'ab':100, 'cd':200}
, então eu gostaria de ver>>> dummy.ab 100
Vou demonstrar como usar um namedtuple
do Módulo collections
para conseguir isto:
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
O Devolve 100
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
Poderá usar o seguinte código para actualizar os atributos da classe utilizando um objecto 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
Simplificar a matemática ou ímpar argumentos, tais como ( start_x, start_y, end_x, end_y ) para ( x, y, w, h ) ie x, y, x + w, y + h ( às vezes exigindo min / max ou se w / h são negativos e a implementação de não gostar, eu vou subtrair x / y e abs w / h. etc.. ).
Anular os getters / setters internos é uma boa maneira de ir, mas o problema é que você precisa fazer isso para cada classe, ou pai a classe para essa base... Isto não funciona para mim, porque prefiro ser livre de escolher as crianças. / pais para 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 como eu acho que é tedioso para introduzir os dados, etc...
A Minha solução requer que você adicione 2 linhas extras acima da sua classe para criar uma classe base para a classe que você deseja adicionar as propriedades e, em seguida, 1 linha, e você tem a opção de adicionar callbacks para controlar os dados, informá-lo quando alterações de dados, restringir os dados que podem ser conjunto baseado no valor e / ou tipo de dados, e muito mais.
Você também tem a opção de usar _object.x, _object.x = valor, _object.GetX (), _object.SetX (valor) e eles são tratados de forma equivalente.
Além disso, os valores são os únicos dados não-estáticos que são atribuídos à instância de classe, mas a propriedade real é atribuída à classe significando as coisas que você não quer repetir, não precisa ser repetido... Você pode atribuir um valor padrão para que o getter não precisa dele cada o tempo, apesar de existir uma opção para substituir a predefinição padrão de valor, e não há outra opção para o getter retorna o raw valor armazenado substituindo padrão retorna ( nota: este método significa o valor bruto só é atribuído quando um valor é atribuído, caso contrário, não é Nenhum - quando o valor é Redefinido, em seguida, atribui Nenhum, etc.. )
Também existem muitas funções auxiliares - a primeira propriedade que é adicionada adiciona 2 ou mais ajudantes à classe para referenciar os valores da instância... Ele são ResetAccessors (_key,.. ) varargs repetido ( tudo pode ser repetido usando o primeiro nome args ) e SetAccessors( _key, _value ) com a opção de mais a ser adicionado para a classe principal para auxiliar na eficiência - aquelas previstas são: a forma de agrupar os acessores juntos, por isso, se você tende a repor um pouco de cada vez, a cada vez, você pode atribuí-los a um grupo e repor o grupo, em vez de repetir o nome chaves de cada vez, e mais.
O valor da instância / bruto armazenado é armazenado em classe., a turma. referências a classe do acessor que detém vars / valores / funções estáticas para a propriedade. _classe. é a propriedade em si que é chamada quando acessada através da classe de instância durante a configuração / obtenção, etc.
A _classe de acesso.__ pontos para a classe, mas porque é interno, precisa ser atribuído na classe, e é por isso que optei por usar __Nome = AccessorFunc( ... ) para atribuí-lo, uma única linha por propriedade com muitos argumentos opcionais a usar ( usando varargs riscados porque eles são mais fáceis e eficientes de identificar e manter )...
Eu também crio um monte de funções, como mencionado, algumas das quais usam a informação da função de acessor para que ela não precisa ser chamada ( como é um pouco inconveniente no momento - agora você precisa usar _classe..FunctionName (_class_ instance, args ) - dei a volta usando a pilha / traço para obter a referência da instância para obter o valor, adicionando as funções que executam isto bit marathon, ou adicionando os acessores ao objeto e usando o self ( chamado assim para apontar que eles são por exemplo e para manter o acesso ao self, a referência de classe AccessorFunc, e outras informações de dentro das definições de funções ).
Ainda não está pronto, mas é um fantástico apoio para os pés. Nota: Se você não usar _ _ Nome = AccessorFunc (... ) para criar as propriedades, Você não terá acesso à chave __ mesmo que eu a Defina dentro da função init. Se o fizeres, então ... não são problemas.Também: Note que o nome e a chave são diferentes... O nome é 'formal', usado na criação do nome da função, e a chave é para armazenamento de dados e acesso. é a primeira classe.x onde minúsculas x é chave, o nome seria maiúsculo X de modo que GetX( ) é a função em vez de Getx () que parece um pouco estranho. isto permite o self.x para trabalhar e olhar apropriado, mas também permitir GetX () e olhar apropriado.
Tenho uma classe de exemplo configurada com chave / nome idêntico, e diferente para mostrar. a lot of helper functions created in order to output the data (Note: Not all of this is complete ) so you can see what is going on.
A lista actual de funções usando a chave: x, nome: x saídas como:
Isto não é de modo algum uma lista abrangente - há alguns que não chegaram a esta lista no momento do posting..._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 a ser produzidos são:
Isto é para uma nova classe criada usando a classe de demonstração sem quaisquer dados atribuídos para além do nome (então pode ser saída ) 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 isto é depois de atribuir todas as propriedades _foo (excepto 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) |
Note que devido a restrições de tipos de dados ou de Valor, alguns dados não foram atribuídos - isto é por design. O setter proíbe que os tipos ou valores de dados inválidos sejam atribuídos, mesmo que sejam atribuídos como um valor padrão ( a menos que você sobrepor o comportamento predefinido de protecção de valores)
O código não foi postado aqui porque não tinha espaço depois dos exemplos e explicações... Também porque vai mudar.Por Favor, a ter em conta: no momento desta publicação, o ficheiro é confuso - isto irá mudar. Mas, se você executá-lo em Texto Sublime e compilá - lo, ou executá-lo a partir de Python, ele irá compilar e cuspir uma tonelada de informações-a parte AccessorDB não é feito ( que será usado para atualizar os Getters de impressão e Getkeyoutput helper functions along with being changed to an Instance function, probably put into a single function and renamed-look for it.. )
Seguinte: nem tudo é necessário para que ele execute - muitas das coisas comentadas na parte inferior é para mais informações usadas para depuração - ele pode não estar lá quando você baixá-lo. Se for, você deve ser capaz de descomentar e recompilar para obter mais informações.
Estou à procura de um trabalho para precisar da minha base de classe: passe, minha classe( MyClassBase):... - se souber de uma solução-publique-a.A única coisa necessária na classe são o __ linhas - a str é para depuração, como é o init - eles podem ser removidos da Classe Demo, mas você vai precisar de comentar ou remover algumas das linhas abaixo ( _foo / 2 / 3 )..
As classes String, Dict e Util no topo são uma parte da minha biblioteca Python - elas não estão completas. Copiei umas coisas da biblioteca e ... criei alguns novos. O código completo irá ligar para a biblioteca completa e irá incluí - lo juntamente com o fornecimento de chamadas atualizadas e remoção do código ( na verdade, o único código que resta será a classe de demonstração e as declarações de impressão-o sistema AccessorFunc será movido para a biblioteca )...
Parte do ficheiro:
##
## 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 = { } )
Esta beleza torna incrivelmente fácil criar novas classes com propriedades dinamicamente adicionadas com funções de acesso / callbacks / tipo de dados / aplicação de valores, etc.
Por agora, o link está em (este link deve refletir alterações ao documento. ): https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0
Também: se você não usar Texto Sublime, eu recomendo sobre o Notepad++, Atom, código Visual, e outros por causa de implementações de thread adequadas tornando-o muito, muito mais rápido de usar... Eu também estou trabalhando em um sistema de mapeamento de código IDE para ele-dê uma olhada: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (adicione Repo primeiro no Gestor de pacotes, depois instale o Plugin - quando a versão 1.0.0 estiver pronta, adicioná-la-ei à lista de plugins principal... )
Espero que esta solução ajude... e, como sempre: Só porque funciona, não faz com que seja correcto. Josh 'Acecool' Moser.A única forma de anexar dinamicamente uma propriedade é criar uma nova classe e a sua instância com a sua nova propriedade.
class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
Eu recentemente tive um problema semelhante, a solução que eu vim com usa __getattr__
e __setattr__
para as propriedades que eu quero a lidar, 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