I can create a function of the following format.
def bar():
if not hasattr(bar, 'c'):
bar.c = 0
bar.c += 1
return bar.c
When run it produces the following output, as intended:
>>> bar()
1
>>> bar()
2
>>> bar()
3
But if I suddenly move this function to a class, Python gives me an error.
class Foo(object):
def bar(self):
if not hasattr(self.bar, 'c'):
self.bar.c = 0
self.bar.c += 1
return self.bar.c
Now I get
>>> foo = Foo()
>>> foo.bar()
...
AttributeError: 'method' object has no attribute 'c'
It's telling me it has no attribute, but I'm trying to create the attribute. What's going on here?
Taken from the python documentation (https://docs.python.org/2/library/stdtypes.html?highlight=instancemethod) "However, since method attributes are actually stored on the underlying function object (meth.im_func), setting method attributes on either bound or unbound methods is disallowed."
In other words, we could have tried Foo.bar.im_func.c = 0, but unfortunately, it is read-only, so it doesn't work.
Therefore, for what you try to accomplish, I would suggest
class Foo(object):
c = 0
def bar(self):
Foo.c += 1
return Foo.c
f = Foo()
print f.bar()
print f.bar()
print f.bar()
Works on python2 and python3.
The only "drawback" is that c would be available to any other method of class Foo.
Related
assuming the simple case where Foo is
class Foo:
def some_func(self):
print('hellow world')
I only have access to the variable func where func is :
func = Foo.some_func
I am trying to get the Foo class name from the variable func
func
Out[6]: <function __main__.Foo.some_func>
func.__class__.__name__
Out[7]: 'function'
I am expecting to get Foo is there anyway to do that?
Python 3 solution:
def get_class_name(func):
return func.__qualname__.split('.')[0]
__qualname__ method actually prints Foo.some_func for func.
Splitting the string by . and taking the first element, it should do the job.
Python 2 & 3 solution:
def get_class_name(func):
return func.__str__().split('.')[0].split()[-1]
Edit:
In Python 3, func.__str__() prints <function Foo.some_func at 0x10c456b70>.
In Python 2, func.__str__() prints <unbound method Foo.some_func>.
I want to have in my class a list of all objects that already been created. In my head it makes sense, because lists in Python only saves the local in memory where the object is. In this way, I could have a object in many lists, if I want.
class foo:
bar = []
def __init__(self):
bar.append(self)
I know this code is wrong, but I know Python wouldn't disappoint me. How could I make it?
EDIT:
Here it is the error message:
NameError Traceback (most recent call last)
<ipython-input-7-b640dd81ce90> in <module>()
----> 1 b = foo()
<ipython-input-6-15f2b8409770> in __init__(self)
2 bar = []
3 def __init__(self):
----> 4 bar.append(self)
NameError: name 'bar' is not defined
I may be misunderstanding, but your example seems to work as you intend:
class Foo:
bar = []
def __init__(self, a):
self.x = a
self.bar.append(self)
obj_one = Foo(1)
obj_two = Foo(2)
objects = Foo.bar
for obj in objects:
print(obj.x)
1
2
Every object will have a copy the list, but as you said, they are just references to objects in memory, so shouldn't be that heavy.
Although Aaron's answer technically works I believe, all you actually need to modify from your original code is the inclusion of self. before your bar.append.
This creates a "static" variable of sorts in your foo class that can be accessed from any instance, or from the class itself.
Here is the code.
>>> class foo:
... bar = []
... def __init__(self):
... self.bar.append(self)
...
>>> a = foo()
>>> id(a)
139971700338984
>>> b = foo()
>>> id(b)
139971700338872
>>> [id(i) for i in foo.bar]
[139971700338984, 139971700338872]
I believe this does what you are trying to achieve:
all_foos = []
class foo:
def __init__(self):
all_foos.append(self)
setattr(self, 'all_foos', all_foos)
foo1 = foo() # Create foo1
foo2 = foo() # Create foo2
print(foo2.all_foos)
Outputs:
[<__main__.foo object at 0x7feeeeb0fc18>, <__main__.foo object at 0x7feeeeb0fb38>]
(The two objects^)
I've been doing some Python, and I realised I Haven't actually know a lot about the property decorator, so I tried making a simple example. This is the code I used:
class foo():
def __init__(self):
self.__test = 0
#property
def test(self):
return self.__test
#test.setter
def test(self, value):
self.__test = value
#test.getter
def test(self):
self.__test += 1
return self.__test
Then I started playing with it in the interactive shell:
>>> bar = foo()
>>> bar.test
1
>>> bar.test
2
So far the object behaved as I expected it to.
Then I tried checking out the setter method
>>> bar.test = 5
>>> bar.test
5
>>> bar.test
5
Weird. For some reason the value of __test wasn't incremented.
>>> bar._foo__test
2
I thought I had set __test to be equal to 5.
What's going on?
The problem is that your foo class is an old style class, descriptors (and as such properties) are only intended to work with new style classes.
From the doc:
Note that descriptors are only invoked for new style objects or classes (a class is new style if it inherits from object or type)
In this case, with an old style class setting bar.test = 5 creates a test attribute in the instance dict, which shadows the property from the class dict:
>>> bar = foo()
>>> foo.__dict__
{'test': <property object at 0x7f302e64c628>, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x7f302e658b18>}
>>> bar.test # test property from class dict is used
1
>>> bar.__dict__
{'_foo__test': 1}
>>> bar.test = 5 # sets test on instance
>>> bar.__dict__
{'test': 5, '_foo__test': 1}
So the solution is simple: make foo a new style class by inheriting from object
In python a function is a first class object. A class can be called. So you can replace a function with a class. But can you make a function behave like a class? Can you add and remove attributes or call inner functions( then called methods) in a function?
I found a way to do this via code inspection.
import inspect
class AddOne(object):
"""class definition"""
def __init__(self, num):
self.num = num
def getResult(self):
"""
class method
"""
def addOneFunc(num):
"inner function"
return num + 1
return addOneFunc(self.num);
if __name__ == '__main__':
two = AddOne(1);
two_src = '\n'.join([line[4:] for line in inspect.getsource(AddOne.getResult).split('\n')])
one_src = '\n'.join([line[4:] for line in two_src.split('\n')
if line[:4] == ' ' and line[4:8] == ' ' or line[4:8] == 'def '])
one_co = compile(one_src, '<string>', 'exec')
exec one_co
print addOneFunc(5)
print addOneFunc.__doc__
But is there a way to access the local variables and functions defined in a class in a more direct way?
EDIT
The question is about how to access the inner structure of python to get a better understanding. Of course I wouldn't do this in normal programming. The question arose when we had a discussion about private variables in python. My opinion was this to be against the philosophy of the language. So someone came up with the example above. At the moment it seems he is right. You cannot access the function inside a function without the inspect module, rendering this function private. With co_varnames we are awfully close because we already have the name of the function. But where is the namespace dictionary to hold the name. If you try to use
getResult.__dict__
it is empty. What I like to have is an answer from python like
function addOneFunc at <0xXXXXXXXXX>
You can consider a function to be an instance of a class that only implements __call__, i.e.
def foo(bar):
return bar
is roughly equivalent to
class Foo(object):
def __call__(self, bar):
return bar
foo = Foo()
Function instances have a __dict__ attribute, so you can freely add new attributes to them.
Adding an attribute to a function can be used, for example, to implement a memoization decorator, which caches previous calls to a function:
def memo(f):
#functools.wraps(f)
def func(*args):
if args not in func.cache: # access attribute
func.cache[args] = f(*args)
return func.cache[args]
func.cache = {} # add attribute
return func
Note that this attribute can also be accessed inside the function, although it can't be defined until after the function.
You could therefore do something like:
>>> def foo(baz):
def multiply(x, n):
return x * n
return multiply(foo.bar(baz), foo.n)
>>> def bar(baz):
return baz
>>> foo.bar = bar
>>> foo.n = 2
>>> foo('baz')
'bazbaz'
>>> foo.bar = len
>>> foo('baz')
6
(although it's possible that nobody would thank you for it!)
Note, however, that multiply, which was not made an attribute of foo, is not accessible from outside the function:
>>> foo.multiply(1, 2)
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
foo.multiply(1, 2)
AttributeError: 'function' object has no attribute 'multiply'
The other question addresses exactly what you're trying to do:
>>> import inspect
>>> import new
>>> class AddOne(object):
"""Class definition."""
def __init__(self, num):
self.num = num
def getResult(self):
"""Class method."""
def addOneFunc(num):
"inner function"
return num + 1
return addOneFunc(self.num)
>>> two = AddOne(1)
>>> for c in two.getResult.func_code.co_consts:
if inspect.iscode(c):
print new.function(c, globals())
<function addOneFunc at 0x0321E930>
Not sure if the following is what you're thinking about, but you can do this:
>>> def f(x):
... print(x)
...
>>> f.a = 1
>>> f.a
1
>>> f(54)
54
>>> f.a = f
>>> f
<function f at 0x7fb03579b320>
>>> f.a
<function f at 0x7fb03579b320>
>>> f.a(2)
2
So you can assign attributes to a function, and those attributes can be variables or functions (note that f.a = f was chosen for simplicity; you can assign f.a to any function of course).
If you want to access the local variables inside the function, I think then it's more difficult, and you may indeed need to revert to introspection. The example below uses the func_code attribute:
>>> def f(x):
... a = 1
... return x * a
...
>>> f.func_code.co_nlocals
2
>>> f.func_code.co_varnames
('x', 'a')
>>> f.func_code.co_consts
(None, 1)
Using python, one can set an attribute of a instance via either of the two methods below:
>>> class Foo(object):
pass
>>> a = Foo()
>>> a.x = 1
>>> a.x
1
>>> setattr(a, 'b', 2)
>>> a.b
2
One can also assign properties via the property decorator.
>>> class Bar(object):
#property
def x(self):
return 0
>>> a = Bar()
>>> a.x
0
My question is, how can I assign a property to an instance?
My intuition was to try something like this...
>>> class Doo(object):
pass
>>> a = Doo()
>>> def k():
return 0
>>> a.m = property(k)
>>> a.m
<property object at 0x0380F540>
... but, I get this weird property object. Similar experimentation yielded similar results. My guess is that properties are more closely related to classes than instances in some respect, but I don't know the inner workings well enough to understand what's going on here.
It is possible to dynamically add properties to a class after it's already created:
class Bar(object):
def x(self):
return 0
setattr(Bar, 'x', property(Bar.x))
print Bar.x
# <property object at 0x04D37270>
print Bar().x
# 0
However, you can't set a property on an instance, only on a class. You can use an instance to do it:
class Bar(object):
def x(self):
return 0
bar = Bar()
setattr(bar.__class__, 'x', property(bar.__class__.x))
print Bar.x
# <property object at 0x04D306F0>
print bar.x
# 0
See How to add property to a class dynamically? for more information.
Properties use descriptors which only work on classes and thus
for all instances. But you could use a combination of a descriptor on
a class that would consult a per-instance function.
>>> class Foo(object):
... #property
... def x(self):
... if 'x' in self.__dict__:
... return self.__dict__['x'](self)
...
>>> a = Foo()
>>> def k(self):
... return 0
...
>>> a.__dict__['x'] = k
>>> a.x
0
You can assign the property directly to the class object:
>>> class Foo(object):
pass
>>> a = Foo()
>>> a.__class__
__main__.Foo
>>> setattr(a.__class__, 'm', property(lambda self: 0))
>>> a.m
0
>>> a.m = 24
AttributeError: can't set attribute
Here we have taken #agf's solution and used a lambda function to define the class property.
class A(object):
pass
a = A()
a.__class__.f = property(lambda self: 57)
a.f # 57
The following post provides more context: https://crosscompute.com/n/jAbsB6OIm6oCCJX9PBIbY5FECFKCClyV/_/Assign%20a%20class%20property%20to%20an%20instance