Reputation: 3979
I often use the idiom '{var_name}'.format(**vars(some_class))
.
However, when I use a property, I cannot use this to get the properties value.
Consider this program:
#!/usr/bin/env python
class Foo(object):
def __init__(self):
self._bar = None
self.baz = 'baz here'
@property
def bar(self):
if not self._bar:
# calculate some value...
self._bar = 'bar here'
return self._bar
if __name__ == '__main__':
foo = Foo()
# works:
print('{baz}'.format(**vars(foo)))
# gives: KeyError: 'bar'
print('{bar}'.format(**vars(foo)))
Question:
Is there a way to make a properties value accessible via **vars(some_class)
?
Upvotes: 9
Views: 1651
Reputation: 398
You can write it into __dict__
of your instance
class Article(object):
def __init__(self):
self.content = ""
self.url = ""
@property
def title(self):
return self.__dict__.get("title", "")
@title.setter
def title(self, title):
self.__dict__["title"] = title
then:
>>> article = Article()
>>> article.title = "Awesome Title"
>>> vars(article)
{'content': '', 'url': '', 'title': 'Awesome Title'}
Upvotes: 0
Reputation: 601649
To do exactly what you asked for, you can write a class that translates item access to attribute access:
class WrapperDict(object):
def __init__(self, obj):
self.obj = obj
def __getitem__(self, key):
return getattr(self.obj, key)
Example:
>>> print('{bar}'.format_map(WrapperDict(Foo())))
bar here
Another rather hacky alternative would be to add
__getitem__ = object.__getattribute__
to the class Foo
and then use the Foo
instance directly:
>>> print('{bar}'.format_map(Foo()))
bar here
I think using attribute access notation is the better solution though.
Upvotes: 1
Reputation: 101959
Short answer: No, it's not possible to use .format(**vars(object))
to do what you want, since properties do not use __dict__
and from vars
documentation:
vars(...)
vars([object])
-> dictionary
- Without arguments, equivalent to
locals()
.- With an argument, equivalent to
object.__dict__
.
However you can achieve what you want using different format specifiers, for example the attribute lookup:
In [2]: '{.bar}'.format(Foo())
Out[2]: 'bar here'
Note that you simply have to add a leading .
(dot) to the names and you get exactly what you want.
Side note: instead of using .format(**vars(object))
you should use the format_map
method:
In [6]: '{baz}'.format_map(vars(Foo()))
Out[6]: 'baz here'
Calling format_map
with a dict
argument is equivalent to calling format
using the **
notation, but it is more efficient, since it doesn't have to do any kind of unpacking before calling the function.
Upvotes: 8
Reputation: 1624
If I understood you correct, something like this should work:
print( eval( 'foo.{bar}'.format( **dict( ( v, v ) for v in dir( foo ) ) ) ) )
But nevertheless this feels somehow "very bad".
Upvotes: 0