Dynamically creating properties?

A

Andy Dingley

I have some XML, with a variable and somewhat unknown structure. I'd
like to encapsulate this in a Python class and expose the text of the
elements within as properties.

How can I dynamically generate properties (or methods) and add them to
my class? I can easily produce a dictionary of the required element
names and their text values, but how do I create new properties at run
time?

Thanks,
 
J

John Gordon

In said:
How can I dynamically generate properties (or methods) and add them to
my class? I can easily produce a dictionary of the required element
names and their text values, but how do I create new properties at run
time?

You can set dynamic attributes on class objects without any special
processing at all. Just do it, like so:

class X(object):
pass

myx = X()

myx.color = 'red'
myx.food = 'french fries'
myx.lucky_number = 7

Or, if you don't know the attribute name beforehand:

setattr(myx, 'occupation', 'programmer')

For methods, use an existing method name (without the trailing parentheses)
as the attribute value, like so:

myx.method = float # assigns the built-in method float()
 
D

DevPlayer

I have some XML, with a variable and somewhat unknown structure. I'd
like to encapsulate this in a Python class and expose the text of the
elements within as properties.

How can I dynamically generate properties (or methods) and add them to
my class?  I can easily produce a dictionary of the required element
names and their text values, but how do I create new properties at run
time?

Thanks,

class MyX(object):
pass
myx = myx()

xml_tag = parse( file.readline() )

# should be a valid python named-reference syntax,
# although any object that can be a valid dict key is allowed.
# generally valid python named reference would be the answer to
your question
attribute = validate( xml_tag )

# dynamicly named property
setattr( myx, attribute, property(get_func, set_func, del_func,
attr_doc) )

# "dynamicly named method"
# really should be a valid python named-reference syntax
myfunc_name = validate(myfunc_name)

def somefunc(x):
return x+x
# or
somefunc = lambda x: x + x

setattr( myx, myfunc_name, somefunc )


So beaware of:
# \\\\\\\\\\\\\\\\\\\\\\\\\
setattr(myx, '1', 'one')

myx.1
File "<input>", line 1
x.1
^
SyntaxError: invalid syntax

# \\\\\\\\\\\\\\\\\\\\\\\\\
x.'1'
File "<input>", line 1
x.'1'
^
SyntaxError: invalid syntax

# \\\\\\\\\\\\\\\\\\\\\\\\\
x.__dict__['1'] # returns
'one'

x.__dict__ # returns
{'1': 'one'}

So you should validate your variable names if you are getting them
from somewhere.
 
D

DevPlayer

Personally I like to use this function instead of a "try: except:"
because try-except will allow names like __metaclass__.

Remember, setattr(obj, attr_name, value) allows attr_name to be any
valid str().
For example: '!@kdafk11', or '1_1', '1e-20', '0.0', '*one', '\n%%',
etc.

def isvalid_named_reference( astring ):
# "varible name" is really a named_reference
# import string # would be cleaner

valid_first_char =
'_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
valid_rest =
'_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

# I think it's ok here for the rare type-check
# as unicode named-references are not allowed
if type(astring) is not str: return False

if len(astring) == 0: return False

if astring[0] not in valid_first_char: return False

for c in astring[1:]:
if c not in valid_rest: return False

# Python keywords not allowed as named references (variable names)
for astr in ['and', 'assert', 'break', 'class', 'continue',
'def', 'del', 'elif', 'else', 'except', 'exec',
'finally', 'for', 'from', 'global', 'if',
'import', 'in', 'is', 'lambda', 'not', 'or',
'pass', 'print', 'raise', 'return', 'try',
'while', 'yield',]:
if astring == astr: return False

# valid names but bad idea
if astring == '__builtins__': return None
if astring == '__metaclass__': return None
for astr in dir(__builtins__):
if astring == astr: return None # use None as a warning

# there might be more like __slots__, and other
# module level effecting special names like '__metaclass__'

return True

Also when using dynamically created "varible names" to check if your
objects have an attribute with that name already.
 
D

DevPlayer

Second error

def isvalid_named_reference( astring ):
# "varible name" is really a named_reference
import __builtin__ # add line
 
L

Lie Ryan

I have some XML, with a variable and somewhat unknown structure. I'd
like to encapsulate this in a Python class and expose the text of the
elements within as properties.

How can I dynamically generate properties (or methods) and add them to
my class? I can easily produce a dictionary of the required element
names and their text values, but how do I create new properties at run
time?

Thanks,

class MyX(object):
pass
myx = myx()

xml_tag = parse( file.readline() )

# should be a valid python named-reference syntax,
# although any object that can be a valid dict key is allowed.
# generally valid python named reference would be the answer to
your question
attribute = validate( xml_tag )

# dynamicly named property
setattr( myx, attribute, property(get_func, set_func, del_func,
attr_doc) )

# "dynamicly named method"
# really should be a valid python named-reference syntax
myfunc_name = validate(myfunc_name)

def somefunc(x):
return x+x
# or
somefunc = lambda x: x + x

setattr( myx, myfunc_name, somefunc )


So beaware of:
# \\\\\\\\\\\\\\\\\\\\\\\\\
setattr(myx, '1', 'one')

myx.1
File "<input>", line 1
x.1
^
SyntaxError: invalid syntax

# \\\\\\\\\\\\\\\\\\\\\\\\\
x.'1'
File "<input>", line 1
x.'1'
^
SyntaxError: invalid syntax

# \\\\\\\\\\\\\\\\\\\\\\\\\
x.__dict__['1'] # returns
'one'

x.__dict__ # returns
{'1': 'one'}

So you should validate your variable names if you are getting them
from somewhere.

XML does not allow attribute names to start with a number, so I doubt
you need to worry about that. In addition, if you also need to
dynamically access attributes and you have zero control of the name, you
can use getattr().
 
S

Steven D'Aprano

def isvalid_named_reference( astring ):
# "varible name" is really a named_reference
# import string # would be cleaner

I don't understand the comment about "variable name".
valid_first_char =
'_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
valid_rest =
'_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

This would be better:

import string
valid_first_char = '_' + string.ascii_letters
valid_rest = string.digits + valid_first_char


# I think it's ok here for the rare type-check
# as unicode named-references are not allowed
if type(astring) is not str: return False

In Python 3 they are:

http://www.python.org/dev/peps/pep-3131/

if len(astring) == 0: return False
if astring[0] not in valid_first_char: return False
for c in astring[1:]:
if c not in valid_rest: return False

# Python keywords not allowed as named references (variable names)
for astr in ['and', 'assert', 'break', 'class', 'continue',
'def', 'del', 'elif', 'else', 'except', 'exec',
'finally', 'for', 'from', 'global', 'if', 'import',
'in', 'is', 'lambda', 'not', 'or', 'pass', 'print',
'raise', 'return', 'try', 'while', 'yield',]:
if astring == astr: return False

You missed 'as' and 'with'. And 'nonlocal' in Python 3. Possibly others.

Try this instead:

from keywords import iskeyword
if iskeyword(astring): return False
 
D

DevPlayer

To be honest, I was hoping someone would have posted a link to a well
known and tested recipe. You'd think this function would be in the
standard library or a specific Exception tied directly with setattr()
and getattr() (and possibly __getattr__(), __getattribute__(),
__setattr__())

The main thing I wanted to point out though is when you start using
dynamically named references, there's more to it then just letting a
dynamic file define it.

If there's a way to reference a set of data, it really shouldn't be
with a "dynamically named reference" too often.

Databases are a good example. Perhaps this is a better way for
example: If you have a bunch of tables in your DB -is- it better to
get the table def and create a Python class with dynamically named
"fields"?

Or is it better to create a Table class with name attribute and a
Field class with a name attribute (named "name")

SO instead of :
field_name = xml_parse.get_next_field_name(xml_table_definition)
my_table = Table()
setattr(my_table, field_name,
empty_list_to_later_contain_field_data)

Perhaps:
field_name = xml_parse.get_next_field_name(xml_table_definition)
my_table = Table()
my_table.fields[field_name] =
empty_list_to_later_contain_field_data
# or
my_table.add_field( Field(field_name) )
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,227
Latest member
Daniella65

Latest Threads

Top