Why I can't instantiate a instance in the same module? - python

Suppose my module is myclass.py, and here is the code:
#!/usr/bin/env python
# coding=utf-8
class A(object):
b = B()
def __init__(self):
pass
class B(object):
pass
and import it
In [1]: import myclass
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-1-e891426834ac> in <module>()
----> 1 import myclass
/home/python/myclass.py in <module>()
2 # coding=utf-8
3
----> 4 class A(object):
5 b = B()
6 def __init__(self):
/home/python/myclass.py in A()
3
4 class A(object):
----> 5 b = B()
6 def __init__(self):
7 pass
NameError: name 'B' is not defined
I know that if I define the class B above the class A, it is ok, there is no error. But, I don't want do that, are there any other methods to solve this. And I know that in C, there is function declaration.Thank you!

The class definition is a statement. When statement AA is executed, The statement of BB is not executed yet. Therefore, There is no class B yet and you get NameError: name 'B' is not defined
class A(object):
b = B() # <== AA
def __init__(self):
pass
class B(object): # <== BB
pass
To fix it:
You can change the order of classes:
class B(object):
pass
class A(object):
b = B()
def __init__(self):
pass
You can move the statement which use the class B to classmethod and call it after the the defintion of class B:
class A(object):
#classmethod
def init(cls):
cls.b = B()
def __init__(self):
pass
class B(object):
pass
A.init()

It should work if you do it like so:
class A(object):
def __init__(self):
self.b = B()
class B(object):
pass
EDIT: You can do it like this if you want to write all the definitions of the class after you have written class A.
class B:
pass
class A(object):
b = B()
def __init__(self):
pass
class B(object):
def __init__(self):
pass
EDIT 2: Ignore the above solution, it doesn't work.

Is there any good reason to do what you are doing? In general this is quite dangerous pattern in Python.
In your case
class A(object):
b = B()
def __init__(self):
pass
You are binding an instance of B to the class A, which means that every instance of class A will share the same instance of class B. It's a case you must then handle properly.
In general you don't want this, If you want each instance of A to be related to an instance of B, you must make the assignment inside __init__
class A(object):
def __init__(self):
self.b = B()
In these case it doesn't meter where class B is defined, since it's instantiated at run time.
Again beware that the semantic is very different in the two cases (if you know Java, the former is more like defining a static attribute).
About:
And I know that in C, there is function declaration
You shouldn't make too much parallels with a language like C, which is very different on many aspects, most important: it's a compiled language, that means that you code is parsed in it whole before being translated to machine language, that's why you can make function declaration and have your namespace populated regardless of the order you define things.
Python is an interpreted language, which means basically that each statement is translated when it's called and a class declaration is called when the module is imported.
So to recap: if you really need a class bound instance, you have to declare class B before class A, else you must instantiate B inside __init__, then you can declare B wherever you want (since it's called at runtime).

Related

Understanding Nested Inheritance in Python

Here is a simplified code of my main code illustrating the behaviour I obtain.
Suppose I have a main class (MAIN) and two classes (A,B) inheriting from it. This main class has a method which is overwriten by A but not by B, which means that B inherits the method from main.
Then I have a class D which inherits from A and from B, and has a method which calls the aforementioned method. From what I have understood in the way multiple inheritance work, if I define D as class D(A,B) then if A and B have a shared method, calling D.method() will call A.method, and vice-versa (i.e if class D(B,A) then B.method is called. The following code exemplifies this text.
class MAIN(object):
def __init__(self):
pass
def print(self):
print('HELLO MAIN')
class A(MAIN):
def __init__(self):
pass
def print(self):
print('HELLO A')
class B(MAIN):
def __init__(self):
pass
class C(A,B):
def __init__(self):
pass
def Cprint(self):
self.print()
c = C()
c.Cprint()
class C(B,A):
def __init__(self):
pass
def Cprint(self):
self.print()
c = C()
c.Cprint()
However this code always print 'HELLO A', i.e even in the case class C(B,A) I don't get a HELLO MAIN as I would expect. What is happening here? Thanks so much in advance
The mro is (C, A, B, MAIN) with class C(A, B) and (C, B, A, MAIN) with class C(B, A). In both cases, A is before MAIN. B doesn't define .print, so it doesn't matter.
The method uplooks works like this: (pseudo code)
def find_attribute(obj, name):
if name in obj.__dict__:
return obj.__dict__[name]
mro = type(obj).__mro__
for cls in mro:
if name in cls.__dict__:
return cls.__dict__[name] # (Here a bit more magic for descriptors happens)
raise AttributeError(name)
For the classes this is what their __dict__ look like:
MAIN.__dict__ = {"print": <method MAIN.print>}
A.__dict__ = {"print": <method A.print>}
B.__dict__ = {}
C.__dict__ = {"Cprint": <method C.Cprint>}
As you can see, B does not have a print defined, so in mro=(C, B, A, MAIN) the first print that does get found is in A.
You are inheriting the Class A everywhere and class A overrrides Main functions print() thats why you dont get the "HELLO MAIN"
class C(B):
def __init__(self):
pass
def Cprint(self):
self.print()
inherit only B class which does not overrides Main class print function then you will get the HELLO MAIN output

In Python, is there any way to call a child class's method override from its parent class?

I'm trying to re-teach myself Python and figure out the specific details, tips and tricks, and common conventions around abstract classes and polymorphism. Right now, I have a class hierarchy that looks like this:
from abc import ABC, abstractmethod
class A(ABC):
#abstractmethod
def x(self):
pass
def do_x_ten_times(self):
for i in range(10):
x()
class B(A):
def x(self):
print("Hello World")
class C(A):
def x(self):
print("Hello StackOverflow")
b = B()
b.x()
c = C()
c.x()
b.do_x_ten_times()
My thinking is that do_x_ten_times() would be the same exact code in both subclasses B and C. So it would be convenient (and my code would be less repetitive) if I could just put the code for do_x_ten_times() in A, and have A call whatever the subclass's implementation of x() is. Unfortunately, I get "NameError: name 'x' is not defined."
I get why might not be typical to do something like this, and my gut says that it probably goes against certain rules of polymorphism. If I really need to, I'm fine copypastying do_x_ten_times() into both classes B and C, and making it abstract in A. But I'm wondering if there's any reasonable way around having to repeat this code.
You need to call self.x() in A.do_x_ten_times()
from abc import ABC, abstractmethod
class A(ABC):
#abstractmethod
def x(self):
raise NotImplementedError
def do_x_ten_times(self):
for i in range(10):
self.x() # <-- self will refer to the calling instance
# implementation of x(self)
class B(A):
def x(self):
print("Hello World")
class C(A):
def x(self):
print("Hello StackOverflow")
b = B()
b.x()
c = C()
c.x()
b.do_x_ten_times()

Get variable name from argument/parameter in class function

I try to get the name of the variable, which I passed to a function.
class A():
def __init__(self):
self.a = 1
class B():
def __init__(self):
self.b = A()
self.c = A()
def doSomething(self, hello):
print(hello)
B().doSomething(B().b)
<__main__.A object at 0x7f67571a3d68>
What I want is that I can identify in the function B().doSomething(), that the variable is b. Is this possible? One restriction is that in the function B().doSomething() only instance variables of B are passed.
For example in peewee (https://github.com/coleifer/peewee), a MySQL ORM in python, they build expressions for filtering like:
B.select().where(B.b == True)
And somehow they are able to identify, that b is passed. Because otherwise the query can not be build properly.
I know they are using static variables in the class, is this maybe the trick?
Thanks for helping! :)
Going by your B().doSomething(B().b) example call I'm going to assume you're attempting to determine if the variable hello is equivalent to the variable b declared on the class B object.
In which case, all you need to do is call the self reference. self refers to the instance of the object that you're working with and every method defined within a class automatically gets reference to the object's self as a method attribute.
Thus, to determine if the the object b variable is equal to the hello parameter all you need to do is if self.b == hello: #do code
B().b is not an instance variable of B; rather, it is an instance variable of A. In your constructor in B, you may have meant self.a to be an instance of B or self.a to be an instance of B. If this is your general idea, you can implement a boolean overloading method to destinguish between the two. In the case of your code, it may be best to create a third class, C, to check what class an attribute that is passed to doSomething belongs to:
class A():
def __init__(self):
self.a = 1
def __bool__(self):
return True
class B():
def __init__(self):
self.b = 1
def __bool__(self):
return False
class C():
def __init__(self):
self.a = A()
self.b = B()
def doSomething(self, hello):
if not hello:
print("instance of a got passed")
else:
print("instance of b got passed")
C().doSomething(C().b)
Output:
instance of b got passed

Python: Call for value inside nested class

I'm trying to call for value from class B that is nested in class A and use it in class C.
I'm getting AttributeError:
class A():
class B():
a = 1
class C():
b = 2
c = B.a + b
AttributeError: class B has no attribute 'a'
I also tried to call From 'A', Pycharm recognize it, but python still get AttributeError:
class A(object):
class B(object):
a = 1
class C(object):
b = 2
c = A.B.a + b
AttributeError: class A has no attribute 'B'
Does someone have an idea of how to use it?
Thanks
The problem is that the class template (A) is not constructed while you're calling A.B.a. That is, A is not bound yet to a class.
Try this workaround:
class A():
class B():
a = 1
Now create C separately (A is already defined):
class C():
b = 2
c = A.B.a + b
And reference C from A:
A.C = C
This can possibly be done via meta-classes, but could be an over-kill here.
At compile time, the class definition for class A is not complete hence you can not access the classes, variables and methods defined in a parent class inside a nested class.
You can try separating the class definitions though as suggested by #Reut Sharabani.
You can not access the class by its name, while the class definition statement is still executed.
class A(object):
class B(object):
a = 1
class C(object):
b = 2
c = A.B.a + b # here class A statement is still executed, there is no A class yet
To solve the problem you must defer the execution of those statements :
move the all those statements to a classmethod
call them after the classes was defined.
class A(object):
class B(object):
#classmethod
def init(cls):
cls.a = 1
class C(object):
#classmethod
def init(cls):
cls.b = 2
cls.c = A.B.a + cls.b
#classmethod
def init(cls):
cls.B.init()
cls.C.init()
A.init()

How can i have 2 classes reference the same instance of a class

I have class A class B and class C.
class A and B can affect class C. so they need to refer to the same instance of the class.
#a.py
from C import C
Cinstance = C()
Cinstance.add()
#b.py
class b(object)
#i need to refer to 'cinstance' here to control the same instance of the class
#C.py
class C(object)
def __init__(self):
self.a=1
def add(self):
self.a += 1
print a
How do i need to import and instanciate the classes for it to work this way? I am new to programming and still learning so things that are obvious are still a little difficult for me right now.
class A:
def __init__(self,cInst):
self.c = cInst
class B:
def __init__(self,cInst):
self.c = cInst
cInst = C()
a = A(cInst)
b = B(cInst)
something like that maybe
based on what you have there I think the easiest thing would be to import Cinstance from module a.
from a import Cinstance
You can pass an instance of A and B to your C.__init__ method and save them as attributes of C.
I'm on my phone, so the code below isn't tested
class C(object):
def __init__(self, a, b):
self.a = a
self.b = b
>>> c = C(A(), B())

Categories