I would like to achieve something similar to this construction:
class Outer:
class A:
foo = 1
class B:
def __init__(self):
self.bar = A.foo
Outer.B().bar # ==> 1
But this fails with
NameError: name 'A' is not defined
I'm not even sure I understand why as A is (I thought) in scope.
Could you help me clarify why this doesn't work and how I could get around it?
Names are looked up only in globals, locals, and nonlocal cells (but you don't have a closure here).
Write Outer.A instead of A, or consider making Outer a module.
It works if you use Outer.A.foo.
Just like what you did for Outer.B().bar: do the same for self.bar=Outer.A().foo
Inner classes in Python do not have acces to the members of the enclosing class. A is not in scope of B as you state. A and B are both in scope of Outer, but they do not know of each other. Therefore a possible solution to your problem:
class Outer:
class A:
foo = 1
class B:
def __init__(self, class_a):
self.bar = class_a.foo
def __init__(self):
self.a = self.A()
self.b = self.B(self.a)
print(Outer.A.foo) # 1
print(Outer.B.bar) # AttributeError: type object 'B' has no attribute 'bar'
outer = Outer()
print(outer.a.foo) # 1
print(outer.b.bar) # 1
Related
I have two questions regarding the code below.
What is the difference between self.a=self.test1() and a=self.test1()? One is class field and other one is object field?
Why cannot I define result = self.a+self.b? How to correct it?
class Test():
def __init__(self):
self.a=self.test1()
a=self.test1()
self.b=Test.test2()
result = self.a+self.b
def test1(self):
a=100
return a
#classmethod
def test2(cls):
b=200
return b
#staticmethod
def test3():
print("Testing3 is calling ")
c=500
return c
self.a = self.test1() creates an instance attribute named a. The attribute will be accessible from the object anywhere you have a reference to the object.
a = self.test1() defines a local variable named a. It will go out of scope once __init__ returns.
result = self.a + self.b doesn't work because it is in a context where self is not defined. self is just the (conventional) name of the first parameter of an instance method. It's not defined in the namespace of the class statement itself.
self.a is a property in this class. It will remain accessible throughout functions in the Test() class. a = self.test1(), however, goes away once __init__(self) finishes, because a is local to __init__(self).
For result = self.a + self.b, I assume you want a variable called result calculated after self.a and self.b is defined? At that indentation level a statement like this is usually not allowed (I could be wrong), usually a declaration of a property of a class happens here.
I am trying to understand variable scopes in Python, most of the things are clear to me except for the part that I don't understand why class variable is not accessible from its method.
In following example mydef1() can't access a, but if a is declared in global scope(outside class definition) it can.
class MyClass1:
a = 25
def mydef1(self):
print(a)
ins1 = MyClass1()
ins1.mydef1()
Output
Traceback (most recent call last):
File "E:\dev\Python\scope_test2.py", line 6, in <module>
ins1.mydef1()
File "E:\dev\Python\scope_test2.py", line 4, in mydef1
print(a)
NameError: name 'a' is not defined
It's important to understand that some of these comments are not equivalent. MyClass.a is a member of the class itself, self.a is a member of the instance of the class.
When you use self.a it will return a from the class, because there is no a on the instance. If there was also an a which was a member of the instance, it would return that instead. Generally the instance a is set using the __init__ constructor. Both of these can exist simultaneously.
class MyClass1:
a = 25
def __init__(self):
self.a = 100
def instance_a(self):
print(self.a)
def change_instance_a(self):
self.a = 5
def class_a(self):
print(MyClass1.a)
def change_class_a(self):
MyClass1.a = 10
# Create two instances
ins1 = MyClass1()
ins2 = MyClass1()
# Both instances have the same Class member a, and the same instance member a
ins1.instance_a()
ins2.instance_a()
ins1.class_a()
ins2.class_a()
# Now lets change instance a on one of our instances
ins1.change_instance_a()
# Print again, see that class a values remain the same, but instance a has
# changed on one instance only
print()
ins1.instance_a()
ins2.instance_a()
ins1.class_a()
ins2.class_a()
# Lets change the class member a on just one instance
ins1.change_class_a()
# Both instances now report that new value for the class member a
print()
ins1.instance_a()
ins2.instance_a()
ins1.class_a()
ins2.class_a()
Short answer: That's Python's scoping rules. Nested functions in Python are lexically scoped, but that doesn't apply to things nested in classes.
class Foo:
a = 25
print(a)
class Bar:
print(a)
The first one prints, but the second is a NameError.
Longer answer:
There is a function closure for class-level variables, but it is all wrapped in __class__. (The main use for this is in the super() magic, which is why it no longer needs arguments in Python 3.)
class MyClass1:
a = 25
def mydef1(self):
print(__class__.a)
ins1 = MyClass1()
ins1.mydef1() # 25
Normally, you'd access such things through the self parameter to allow subclasses to override them, but __class__ would even work for a staticmethod, which has neither self, nor cls.
class MyClass1:
a = 25
#staticmethod
def mydef1():
print(__class__.a)
ins1 = MyClass1()
ins1.mydef1() # 25
The class object technically doesn't even exist until after the class declaration finishes executing, that's why you can't do
class Foo:
a = 25
class Bar:
# NameError: free variable '__class__' referenced before assignment
print(__class__.a)
Nor even,
class Foo:
a = 25
def bar():
print(__class__.a)
# NameError: free variable '__class__' referenced before assignment in enclosing scope
bar()
You can, however, access the locals() dict before then.
class Foo:
a = 21
locals()['a'] *= 2
Foo.a # 42
So this works.
class Foo:
a = 25
global foolocals
foolocals = locals()
def bar():
print(foolocals['a'])
bar() # 25
Consider the following class:
class ScopeTest(object):
classvariable = 0
number_of_objects_created = 0
def __init__(self, value=1):
self.instancevariable = value
ScopeTest.number_of_objects_created += 1
def number_of_brothers(self):
print(ScopeTest.number_of_objects_created)
def confusion(self, value=2):
self.classvariable = value
print (f"class: {ScopeTest.classvariable}, self:{self.classvariable}")
And let's see what happens when you play around with it:
>>> a = ScopeTest()
>>> a.instancevariable
1
>>> a.classvariable
0
>>> ScopeTest.classvariable
0
So far so good, but when you assign a new attribute to a:
>>> a.classvariable = 2
>>> a.classvariable
2
>>> ScopeTest.classvariable
0
The same holds if you add the attribute inside a class's method:
>>> a.confusion(4)
class: 0, self:4
These kind of class attributes are good to keep things common to all objects, as the number_of_objects_created:
>>> b = ScopeTest()
>>> b.number_of_brothers()
2
>>> a.number_of_brothers()
2
You could get a little more from this by adding yet another method to the class:
class ScopeTest(object):
...
def other_function(self, classvariable=3):
print(f"class: {ScopeTest.classvariable}\t"
f"instance: {self.classvariable}\t"
f"argument:{classvariable}")
And calling it (after using the first 'confusion' method to set self.classvariable):
>>> a.confusion()
class: 0, self:2
>>> a.other_function()
class: 0 instance: 2 argument:3
By calling print(a) in mydef1, python is looking for a local (or global, as you discovered) variable "a". That is, a variable not directly related to MyClass1 in any way, but such a variable has not been defined yet.
If you're trying to access the class variable "a" (i.e. a is a member of the class itself, not any instance of it), you must use MyClass1.a. Alternatively, because there is no instance variable named "a", you can also use self.a to the same effect. However, as soon as self.a is explicitly defined, self.a == MyClass1.a may not be true. For example:
>>>class MyClass1:
... a = 25
...
>>>my_obj = MyClass1()
>>>MyClass1.a
25
>>>my_obj.a
25
>>>MyClass1.a += 1
>>>MyClass1.a
26
>>>my_obj.a
26
>>>my_obj.a = 5 # explicitly define "a" for this instance; my_obj.a not longer synonymous with MyClass1.a
>>>MyClass1.a += 1
>>>MyClass1.a
27
>>>my_obj.a
5
You need self.a. There is no implicit class scope.
I have a class Foo which is instantiated an indefinite number of times during my program sequence. Like so:
def main():
f = Foo()
while f.run():
del f
f = Foo()
with run() being a method that runs an decisive condition for keeping the program alive.
Now, my Foo class creates on its __init__ method two objects a and b:
Foo class
class Foo:
def __init__(self):
a = A()
b = B(a.var)
I'm looking for a way to a being declared only at the first Foo instantiation and use that same first-instantiated a at the other Foo instantiations.
Problem arises because b depends on a. I thought about a couple solutions - from playing with __new__ and __init__ to override __del__ and global variable as cache - but none of them worked.
note: A needs to be at the same module as Foo
Maybe using a class variable?
class Foo:
a = None
def __init__(self):
if not Foo.a:
Foo.a = A()
b = B(Foo.a.var)
And function B needs to check whether a is None.
If I understand you correctly, you should be able to just make a a class variable.
class Foo:
a = A()
def __init__(self):
b = B(Foo.a.var)
I'm afraid some of your requirements will make Foo extremely difficult to test. Instead, I would suggest that you move some of the dependencies from your constructor to a start class method that would be responsible for creating the initial A instance (at the same module as Foo) and then reusing that instance in a refresh method.
class Foo:
def __init__(self, a, b):
self.a = a
self.b = b
#classmethod
def start(cls):
a = A()
b = B(a.var)
return cls(a, b)
def refresh(self):
b = B(self.a.var)
return self.__class__(self.a, b)
Then, your main function would look something like:
def main():
f = Foo.start()
while f.run():
f = f.refresh()
By overwriting the f variable, you are effectively deleting the reference to the old instance which will eventually be garbage collected.
I want to use the variables i have declared inside a function in one class, in another class.
For example i want to use the variable "j" in another class. Is it possible? (I read somewhere that it might have something to do with instance variables but fully couldn't understand the concept).
class check1:
def helloworld(self):
j = 5
class check1:
def helloworld(self):
self.j = 5
check_instance=check1()
print (hasattr(check_instance,'j')) #False -- j hasn't been set on check_instance yet
check_instance.helloworld() #add j attribute to check_instance
print(check_instance.j) #prints 5
but you don't need a method to assign a new attribute to a class instance...
check_instance.k=6 #this works just fine.
Now you can use check_instance.j (or check_instance.k) just like you would use any other variable.
This may seems a little bit like magic until you learn that:
check_instance.helloworld()
is completely equivalent to:
check1.helloworld(check_instance)
(If you think about it a little bit, that explains what the self parameter is).
I'm not completely sure what you're trying to achieve here -- There are also class variables which are shared by all instances of the class...
class Foo(object):
#define foolist at the class level
#(not at the instance level as self.foolist would be defined in a method)
foolist=[]
A=Foo()
B=Foo()
A.foolist.append("bar")
print (B.foolist) # ["bar"]
print (A.foolist is B.foolist) #True -- A and B are sharing the same foolist variable.
j cannot be seen by another class; however, I think you meant self.j, which can.
class A(object):
def __init__(self, x):
self.x = x
class B(object):
def __init__(self):
self.sum = 0
def addA(self, a):
self.sum += a.x
a = A(4)
b = B()
b.addA(a) # b.sum = 4
Using class inheritane it is very easy to "share" instance variables
example:
class A:
def __init__(self):
self.a = 10
def retb(self):
return self.b
class B(A):
def __init__(self):
A.__init__(self)
self.b = self.a
o = B()
print o.a
print o.b
print o.retb()
Dealing with classes (nested etc) does not look easy in Python, surprisingly! The following problem appeared to me recently and took several hours (try, search ...) without success. I read most of SO related links but none of them has pointed the issue presented here!
#------------------------------------
class A:
def __init__(self):
self.a = 'a'
print self.a
class B(A):
def __init__(self):
self.b = 'b'
A.a = 'a_b'
print self.b, A.a
#------------------------------------
class C:
class A:
def __init__(self):
self.a = 'a'
print self.a
class B(A):
def __init__(self):
self.b = 'b'
A.a = 'a_b'
print self.b, A.a
#------------------------------------
#------------------------------------
>>> c1 = A()
a
>>> c1.a
'a'
>>> c2 = B()
b
>>> c2.a, c2.b
('a_b', 'b')
>>> c3 = C()
>>> c4 = c3.A()
a
>>> c4.a
'a'
>>> c5 = c3.B()
b a_b
>>> c5.b
'b'
>>> c5.a
Traceback (most recent call last):
File "", line 1, in
AttributeError: B instance has no attribute 'a'
Where is the problem in the code?
AND
In both cases it seems that when B(A) is initialized A() is not initialized. What is the solution for this issue? Note that the term A.__init__() being called inside B()'s __init__() does not work!
Updates:
class Geometry:
class Curve:
def __init__(self,c=1):
self.c = c #curvature parameter
print 'Curvature %g'%self.c
pass #some codes
class Line(Curve):
def __init__(self):
Geometry.Curve.__init__(self,0) #the key point
pass #some codes
g = Geometry()
C = g.Curve(0.5)
L = g.Line()
which results in:
Curvature 0.5
Curvature 0
what I was looking for.
The code executed in a method runs in the local scope of that method. If you access an object that is not in this scope, Python will look it up in the global/module scope, NOT in the class scope or the scope of any enclosing class!
This means that:
A.a = 'a_b'
inside C.B.__init__ will set the class attribute of the global A class, not C.A as you probably intended. For that you would have to do this:
C.A.a = 'a_b'
Also, Python will not call parent methods if you override them in subclasses. You have to do it yourself.
The scoping rules mean that if you wanted to call the __init__ method of the parent class inside C.B.__init__, it has to look like this:
C.A.__init__(self)
and NOT like this:
A.__init__(self)
which is probably what you've tried.
Nested classes seems so unpythonic, even if considered as factories. But to answer your question: There simply is no c5.a (instance of C.B). In the init-method of C.B you add to the CLASS C.A an attribute a, but not to C.B! The class A does already have an attribute a, if instantiated! But the object of class B (and even the class) doesn't!
You must also keep in mind, that __init__ is not an constructor like in C++ or Java! The "real constructor" in python would be __new__. __init__ just initializes the instance of a class!
class A:
c = 'class-attribute'
def __init__(self):
self.i = 'instance-attribute'
So in this example c is a class-attribute, where i is an attribute of the instance.
Even more curios, is your attempt to add an attribute to the baseclass at the moment of the instantiation of the child-class. You are not getting a "late" inheritance-attribute that way.
You simply add to the class A an additional attribute, which surprises me to even work. I guess you are using python 3.x?
The reason for this behaviour? Well, i guess it has to do with pythons neat feature that in python definitions are executed(AFAIK).
The same reason why:
def method(lst = []):
is almost ever a bad idea. the deafult-parameter gets bound at the moment of the definition and you won't generate a new list-object every-time you call the method, but reusing the same list-object.