Python properties and string formatting - python

I was under the impression python string formatting using .format() would correctly use properties, instead I get the default behaviour of an object being string-formatted:
>>> def get(): return "Blah"
>>> a = property(get)
>>> "{a}!".format(a=a)
'<property object at 0x221df18>!'
Is this the intended behaviour, and if so what's a good way to implement a special behaviour for properties (eg, the above test would return "Blah!" instead)?

property objects are descriptors. As such, they don't have any special abilities unless accessed through a class.
something like:
class Foo(object):
#property
def blah(self):
return "Cheddar Cheese!"
a = Foo()
print('{a.blah}'.format(a=a))
should work. (You'll see Cheddar Cheese! printed)

Yes, this is basically the same as if you just did:
>>> def get(): return "Blah"
>>> a = property(get)
>>> print a
If you want "Blah" just call the function:
>>> def get(): return "Blah"
>>> a = property(get)
>>> "{a}!".format(a=a.fget())

Python properties do interop well with .format(). Consider the following example:
>>> class Example(object):
... def __init__(self):
... self._x = 'Blah'
... def getx(self): return self._x
... def setx(self, value): self._x = value
... def delx(self): del self._x
... x = property(getx,setx,delx, "I'm the 'x' property.")
...
>>>
>>> ex = Example()
>>> ex.x
'Blah'
>>> print(ex.x)
'Blah'
>>> "{x.x}!".format(x=ex)
'Blah!'
I believe your problem stems from your property not being part of a class. How are you actually using properties that they aren't working with .format()?

Related

How do I change the representation of a Python function?

>>> def hehe():
... return "spam"
...
>>> repr(hehe)
'<function hehe at 0x7fe5624e29b0>'
I want to have:
>>> repr(hehe)
'hehe function created by awesome programmer'
How do I do that? Putting __repr__ inside hehe function does not work.
EDIT:
In case you guys are wondering why I want to do this:
>>> defaultdict(hehe)
defaultdict(<function hehe at 0x7f0e0e252280>, {})
I just don't like the way it shows here.
No, you cannot change the representation of a function object; if you wanted to add documentation, you'd add a docstring:
def foo():
"""Frob the bar baz"""
and access that with help(foo) or print foo.__doc__.
You can create a callable object with a custom __repr__, which acts just like a function:
class MyCallable(object):
def __call__(self):
return "spam"
def __repr__(self):
return 'hehe function created by awesome programmer'
Demo:
>>> class MyCallable(object):
... def __call__(self):
... return "spam"
... def __repr__(self):
... return 'hehe function created by awesome programmer'
...
>>> hehe = MyCallable()
>>> hehe
hehe function created by awesome programmer
>>> hehe()
'spam'
Usually, when you want to change something about the function, say function signature, function behavior or function attributes, you should consider using a decorator. So here is how you might implement what you want:
class change_repr(object):
def __init__(self, functor):
self.functor = functor
# lets copy some key attributes from the original function
self.__name__ = functor.__name__
self.__doc__ = functor.__doc__
def __call__(self, *args, **kwargs):
return self.functor(*args, **kwargs)
def __repr__(self):
return '<function %s created by ...>' % self.functor.__name__
#change_repr
def f():
return 'spam'
print f() # spam
print repr(f) # <function hehe created by ...>
Note, that you can only use class based decorator, since you need to override __repr__ method, which you can't do with a function object.
Not directly the answer to your question, but perhaps you really want a docstring?
>>> def hehe():
... '''hehe function created by awesome programmer'''
... return 'spam'
...
>>> help(hehe)
Help on function hehe in module __main__:
hehe()
hehe function created by awesome programmer
Here's a slightly more flexible version of what's in Alexander Zhukov's answer:
def representation(repr_text):
class Decorator(object):
def __init__(self, functor):
self.functor = functor
def __call__(self, *args, **kwargs):
return self.functor(*args, **kwargs)
def __repr__(self):
return (repr_text % self.functor.__name__ if '%' in repr_text
else repr_text)
return Decorator
from collections import defaultdict
#representation('<function %s created by awesome programmer>')
def f():
return list
dd = defaultdict(f)
print repr(dd)
Output:
defaultdict(<function f created by awesome programmer>, {})
Sincerepresentation()returns a decorator, if you wanted the same boilerplate on several functions you could do something like this:
myrepr = representation('<function %s created by awesome programmer>')
#myrepr
def f():
...
#myrepr
def g():
...
etc

python exec, add staticmethod to class

In python I can do:
exec(code_string, globals(), some_object.__dict__)
to add a method to an object. Is it possible to add a static method to a class in some sort of similar fashion? Like:
exec(code_string, globals(), ClassName.__dict__)
So that I could then statically call the method:
ClassName.some_static_method()
What I'm trying to do is add new staticmethods during runtime given some python code defining the method. i.e. if I was given:
code_string = '''
#staticmethod
def test():
return 'blah'
'''
how can I create and instantiate this into a class so that I could call it?
Hopefully I was clear enough, thank you!
EDIT:
working example of adding a function to an object:
class TestObject(object):
pass
code_string = '''
def test():
return 'blah'
'''
t = TestObject()
exec(code_string, globals(), t.__dict__)
Use setattr()
>>> code_string = '''
... #staticmethod
... def test():
... return 'returning blah'
... '''
>>>
>>> exec(code_string)
>>> test
<staticmethod object at 0x10fd25c58>
>>> class ClassName(object):
... def instancemethod(self):
... print "instancemethod!"
...
>>> setattr(ClassName, 'teststaticmethod', test)
>>> ClassName.teststaticmethod()
'returning blah'
>>>
And here's an article on being safe with exec() and eval() in python.

Ways to set per instance __call__?

What I want to do is something like:
class Foo(object):
def __init__(self):
pass
def f(self):
print "f"
def g(self):
print "g"
# programatically set the "default" operation
fer=Foo()
fer.__call__=fer.f
# a different instance does something else as its
# default operation
ger=Foo()
ger.__call__=ger.g
fer() # invoke different functions on different
ger() # objects depending on how they were set up.
But as of 2.7 (which I'm currently using) I can't do this, the attempts at fer()
raise an exception.
Is there a way to, in effect, set a per instance __call__ method?
The normal stuff with types.MethodType unfortunately doesn't work here since __call__ is a special method.
From the data model:
Class instances are callable only when the class has a __call__() method; x(arguments) is a shorthand for x.__call__(arguments).
This is slightly ambiguous as to what is actually called, but it's clear that your class needs to have a __call__ method.
You'll need to create some sort of hack:
class Foo(object):
def __init__(self):
pass
def f(self):
print "f"
def g(self):
print "g"
def __call__(self):
return self.__call__()
f = Foo()
f.__call__ = f.f
f()
g = Foo()
g.__call__ = g.g
g()
Careful with this though, it'll result in an infinite recursion if you don't set a __call__ on an instance before you try to call it.
Note that I don't actually recommend calling the magic attribute that you rebind __call__. The point here is to demonstrate that python translates: f() into f.__class__.__call__(f) and so there's nothing you can do to change it on a per-instance basis. the class's __call__ will be called no matter what you do -- You just need to do something to change the behavior of the class's __call__ per-instance which is easily achieved.
You could use a setter type thing to actually create methods on your class (rather than simple functions) -- and of course that could be turned into a property:
import types
class Foo(object):
def __init__(self):
pass
def f(self):
print "f"
def g(self):
print "g"
def set_func(self,f):
self.func = types.MethodType(f,self)
def __call__(self,*args,**kwargs):
self.func(*args,**kwargs)
f = Foo()
f.set_func(Foo.f)
f()
def another_func(self,*args):
print args
f.set_func(another_func)
f(1,2,3,"bar")
You might be trying to solve the wrong problem.
Since python allows procedural creation of classes you could write code like that:
>>> def create_class(cb):
... class Foo(object):
... __call__ = cb
... return Foo
...
>>> Foo1 = create_class(lambda self: 42)
>>> foo1 = Foo1()
>>> foo1()
>>> Foo2 = create_class(lambda self: self.__class__.__name__)
>>> foo2 = Foo2()
>>> foo2()
Please note thought that Foo1 and Foo2 do not have a common base class in this case. So isinstance and issubclass will not work. If you need them to have a common base class I would go for the following code:
>>> class Foo(object):
... #classmethod
... def create_subclass(cls, cb):
... class SubFoo(cls):
... __call__ = cb
... return SubFoo
...
>>> Foo1 = Foo.create_subclass(lambda self: 42)
>>> foo1 = Foo1()
>>> foo1()
>>> Foo2 = Foo.create_subclass(lambda self: self.__class__.__name__)
>>> foo1 = Foo2()
>>> foo2()
'Foo'
>>> issubclass(Foo1, Foo)
True
>>> issubclass(Foo2, Foo)
True
I really like the second way as it provides a clean class hierarchy and looks quite clean to me.
Possible solution:
class Foo(object):
def __init__(self):
self._callable = lambda s: None
def f(self):
print "f"
def set_callable(self, func):
self._callable = func
def g(self):
print "g"
def __call__(self):
return self._callable()
d = Foo()
d.set_callable(d.g)

'Can't set attribute' with new-style properties in Python

I'm trying to use new-style properties declaration:
class C(object):
def __init__(self):
self._x = 0
#property
def x(self):
print 'getting'
return self._x
#x.setter
def set_x(self, value):
print 'setting'
self._x = value
if __name__ == '__main__':
c = C()
print c.x
c.x = 10
print c.x
and see the following in console:
pydev debugger: starting
getting
0
File "\test.py", line 55, in <module>
c.x = 10
AttributeError: can't set attribute
what am I doing wrong?
P.S.: Old-style declaration works fine.
The documentation says the following about using decorator form of property:
Be sure to give the additional functions the same name as the original property (x in this case.)
I have no idea why this is since if you use property as function to return an attribute the methods can be called whatever you like.
So you need to change your code to the following:
#x.setter
def x(self, value):
'setting'
self._x = value
The setter method has to have the same name as the getter. Don't worry, the decorator knows how to tell them apart.
#x.setter
def x(self, value):
...
When you call #x.setter, #x.getter, or #x.deleter, you're creating a new property object and giving it the name of the function you're decorating. So really, all that matters is that the last time you use a #x.*er decorator in the class definition, it has the name you actually want to use. But since this would leave your class namespace polluted with incomplete versions of the property you wish to use, it's best to use the same name to clean them up.
If you don't want the extra _x name slot, here's a complex little trick you can do:
(tested with Py34)
>>> class C(object):
__slots__ = ['x'] # create a member_descriptor
def __init__( self ):
self.x = 0
# or use this if you don't want to call x_setter:
#set_x( self, 0 )
>>> get_x = C.x.__get__ # member_descriptor getter
>>> set_x = C.x.__set__ # member_descriptor setter
>>> # define custom wrappers:
>>> def x_getter( self ):
print('getting')
return get_x( self )
>>> def x_setter( self, value ):
print('setting')
set_x( self, value )
>>> C.x = property( x_getter, x_setter ) # replace the member_descriptor
>>> c = C()
setting
>>> print(c.x)
getting
0
>>> c.x = 10
setting
>>>

Python __str__: Magic Console

Suppose one decided (yes, this is horrible) to create handle input in the following manner: A user types in a command on the python console after importing your class, the command is actually a class name, the class name's __str__ function is actually a function with side effects (e.g. the command is "north" and the function changes some global variables and then returns text describing your current location). Obviously this is a stupid thing to do, but how would you do it (if possible)?
Note that the basic question is how to define the __str__ method for a class without creating an instance of the class, otherwise it would be simple (but still just as crazy:
class ff:
def __str__(self):
#do fun side effects
return "fun text string"
ginst = ff()
>>ginst
What you are looking for is the metaclass
class Magic(type):
def __str__(self):
return 'Something crazy'
def __repr__(self):
return 'Another craziness'
class Foo(object):
__metaclass__ = Magic
>>> print Foo
Something crazy
>>> Foo
Another craziness
in console you're getting representation of your object, which __repr__ is responsible for. __str__ used for printing:
>>> class A:
def __str__(self):
return 'spam'
>>> A()
<__main__.A object at 0x0107E3D0>
>>> print(A())
spam
>>> class B:
def __repr__(self):
return 'ham'
>>> B()
ham
>>> print(B())
ham
>>> class C:
def __str__(self):
return 'spam'
def __repr__(self):
return 'ham'
>>> C()
ham
>>> print(C())
spam
You could use instances of a class rather than classes themselves. Something like
class MagicConsole(object):
def __init__(self, f):
self.__f = f
def __repr__(self):
return self.__f()
north = MagicConsole(some_function_for_north)
south = MagicConsole(some_function_for_south)
# etc

Categories