I am fairly new to python, and noticed these posts:
Python __init__ and self what do they do? and
Python Classes without using def __init__(self)
After playing around with it, however, I noticed that these two classes give apparently equivalent results-
class A(object):
def __init__(self):
self.x = 'Hello'
def method_a(self, foo):
print self.x + ' ' + foo
(from this question)
and
class B(object):
x = 'Hello'
def method_b(self,foo):
print self.x + ' ' + foo
Is there any real difference between these two? Or, more generally, does __init__ change anything inherently about the attributes of a class? In the documentation it is mentioned that __init__ is called when the instance is created. Does this mean that x in class B is established before instantiation?
Yeah, check this out:
class A(object):
def __init__(self):
self.lst = []
class B(object):
lst = []
and now try:
>>> x = B()
>>> y = B()
>>> x.lst.append(1)
>>> y.lst.append(2)
>>> x.lst
[1, 2]
>>> x.lst is y.lst
True
and this:
>>> x = A()
>>> y = A()
>>> x.lst.append(1)
>>> y.lst.append(2)
>>> x.lst
[1]
>>> x.lst is y.lst
False
Does this mean that x in class B is established before instantiation?
Yes, it's a class attribute (it is shared between instances). While in class A it's an instance attribute. It just happens that strings are immutable, thus there is no real difference in your scenario (except that class B uses less memory, because it defines only one string for all instances). But there is a huge one in my example.
In the first exemple you have the variable of the instance of the class. This variable is only accessible through an instance (self required).
class A():
def __init__(self):
self.x = 'hello'
print A.x -> AttributeError
print A().x -> 'hello'
In the second exemple you have a static variable. You can access to this variable thanks to the name of the class A
class A():
x = 'hello'
print A.x -> 'hello'
print A().x -> 'hello'
In fact you can have a static variable and an instance variable with the same name:
class A():
x = 'hello'
def __init__(self):
self.x = 'world'
print A.x -> hello
print A().x -> world
The static value is shared between all the instances
class A():
x = 'hello'
#staticmethod
def talk():
print A.x
a = A()
print a.talk() -> hello
A.x = 'world'
print a.talk() -> world
You have a good article here:
http://linuxwell.com/2011/07/21/static-variables-and-methods-in-python/
As others have stated, it's the difference between a variable on a class and a variable on a class instance. See the following example.
>>> class A:
... a = []
...
>>> class B:
... def __init__(self):
... self.b = []
...
>>> a1 = A()
>>> a1.a.append('hello')
>>> a2 = A()
>>> a2.a
['hello']
>>> b1 = B()
>>> b1.b.append('goodbye')
>>> b2 = B()
>>> b2.b
[]
For immutable objects like tuples, strings, etc. it's harder to notice the difference, but for mutables, it changes everything—the changes applied are shared between ALL instances of that class.
Note also that the same behavior happens for keyword argument defaults!
>>> class A:
... def __init__(self, a=[]):
... a.append('hello')
... print(a)
...
>>> A()
['hello']
>>> A()
['hello', 'hello']
>>> A()
['hello', 'hello', 'hello']
>>> class B:
... def __init__(self, b=None):
... if b is None:
... b = []
... b.append('goodbye')
... print(b)
...
>>> B()
['goodbye']
>>> B()
['goodbye']
>>> B()
['goodbye']
This behavior bites a lot of new Python programmers. Good for you in discovering these distinctions early on!
Related
I was trying to test whether a class had a certain function as a variable, and to test was doing so by comparing it to the parents function that the child class was initialised with, but found that they existed at differing memory addresses. The following code exhibits this behaviour:
>>> class common():
def a():
print('hello')
>>> class one(common):
def __init__(self):
self.fn = super().a
>>> var1 = one()
>>> var2 = one()
>>> var1.fn == var2.fn
False
>>> var1.fn is var2.fn
False
>>> var1.fn
<bound method common.a of <__main__.one object at 0x0737CC50>>
>>> var2.fn
<bound method common.a of <__main__.one object at 0x0737CAF0>>
Why is this? And is there any way around it so I can check if they are equivalent?
Edit:
And why should the above be any different to below?:
>>> def h(x):
return x+2
>>> var1 = h
>>> var2 = h
>>> var1 == var2
True
>>> var1 is var2
True
>>> var1
<function h at 0x073824F8>
>>> var2
<function h at 0x073824F8>
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 have :
class A:
def a():
pass
After typing in the python command line:
Aobj = A()
aBoundMeth = getattr(Aobj, 'a')
My goal is to get the name of the method that aBoundMeth object represents. Is it possible to do it?
Thank you in advance.
Assuming that the name of the method is the string 'a' (in this case), You can use the __name__ attribute on the function object.
e.g.
>>> Aobj = A()
>>> aBoundMeth = getattr(Aobj, 'a')
>>> aBoundMeth.__name__
'a'
Note that this is the function name when it was created. You can make more references to the same function, but the name doesn't change. e.g.
>>> class A(object):
... def a(self):
... pass
... b = a
...
>>> Aobj = A()
>>> Aobj.a.__name__
'a'
>>> Aobj.b.__name__
'a'
Let's say you have functions a and b. Can you, by calling function a, replace function b with another function?
I know you can do b=a(b), but can you change b with just doing a(b)?
Functions and methods are attributes of modules and classes respectively. So you can modify the required attribute to point to another function.
>>> __name__
'__main__'
>>> def a():
... print 'a'
...
>>> def b():
... print 'b'
...
>>> import sys
>>> sys.modules['__main__'].b = a
>>> b()
a
For a class:
>>> class A(object):
... def a(self):
... print 'a'
... def b(self):
... print 'b'
...
>>> A.b = A.a
>>> x = A()
>>> x.b()
a
And if you have a variable holding the name to replace, then use setattr, for example:
setattr(sys.modules['__main__'],'b',a)
setattr(A,'b',A.a)
Just replace the string ('b') with your variable.
Not sure why you would want to though? It will make for confusing code.
You can replace the code object of b with another function's code:
>>> def b():
... print 'b'
...
>>> def a():
... print 'a'
...
>>> def c(f):
... f.__code__ = a.__code__ # func_code instead of __code__ for py 2.5 and earlier
...
>>> c(b)
>>> b()
a
Although this is almost certainly a sign you're thinking of whatever problem you're trying to solve in the wrong way.
You could use global to accomplish something like this.
>>> def a():
... global b
... def b():
... print "New Function B"
>>> def b():
... print "Function B"
...
...
>>> b()
Function B
>>> a()
>>> b()
New Function B
>>>
If you don't know which method you're replacing, you can use function.func_code to replace one function's code with another.
>>> def change(old_func, new_func):
... old_func.func_code = new_func.func_code
...
>>> def a():
... print "Function A"
...
>>> def b():
... print "Function B"
...
>>> def c():
... print "Function C"
...
>>>
>>> change(a, b)
>>> a()
Function B
>>> change(a, c)
>>> a()
Function C
>>>
You can do it easily like this
def a(func):
globals()[func] = c
def b():
print('b')
def c():
print('c')
a()
b()
>>> c
I'm not sure what you are aiming for, but yes, you can do that:
>>> def a():
... global b
... b = a
... return 'a'
...
>>> def b():
... return 'b'
...
>>> b()
'b'
>>> a()
'a'
>>> b()
'a'
>>> def c(f):
What if I don't know I'm overwriting b beforehand?In other words, if I want to pass b as an argument to a
>>> def c(f):
... global b
... b = f
... return 'c'
...
>>> def d():
... return 'd'
...
>>> b()
'a'
>>> c(d)
'c'
>>> b()
'd'
>>> c(a)
'c'
>>> b()
'a'
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