How to check if an instance attribute exists in an method Python - python

I will get straight to the point. I have been trying to find different ways in which I can check if an instance attribute exists in a method of a class which has not been called from the instance of the class.
Consider the following example:
class Main:
def thing1(self):
self.x = 10
m = Main()
print(m.x)
This code above will not work, until I call the method, e.g:
class Main:
def thing1(self):
self.x = 10
m = Main()
m.thing1()
print(m.x)
or
class Main:
def __init__(self):
self.thing1()
def thing1(self):
self.x = 10
m = Main()
print(m.x)
I want to know if there is a way I can check if this instance attribute exists in the method without having to call it.
I have tried using:
hasattr()
dir()
__ dict __
None of them will show that I have a instance attribute called 'x' in thing1
As well as this, I would greatly appreciate it if you could show me how I could check if an instance attribute in a method exists through another method.
class Main:
def thing1(self):
self.x = 10
def check(self):
# Is there a way I can check if 'x' exists in the method names thing1, without having to call it.
m = Main()
Thank you for taking the time to read this question, and I hope that you have a great day!

Indeed hasattr() definitely works. See interactive example below:
>>> class Main:
... def thing1(self):
... self.x = 10
...
>>> m = Main()
>>> hasattr(m, 'x')
False
>>> m.thing1()
>>> hasattr(m, 'x')
True
>>> print(m.x)
10
It's worth noting, though, that attributes should be initialized in the constructor. It's good practice to use __slots__ to declare attributes, but they must still be initialized. Also, class variables (attributes) are initialized outside the constructor. See example below:
class Main:
c = 5
__slots__ = ('x', 'y')
def __init__(self):
self.y = 12
def thing1(self):
self.x = 10
m = Main()
print(hasattr(m, 'x'))
print(hasattr(m, 'y'))
print(m.c)
Output:
False
True
5
After reading the question again and additional comments, it seems that you want to access an attribute that is initalized in a method, without actually calling said method. This is not possible because the attribute won't actually exist until it is initialized.
The only way I can think of to determine if the attribute would exist after the method call, is to read the code of the function in your program and check for the presence of the attribute. This is one crude example, which by no means is guaranteed to always work:
import inspect
lines = inspect.getsource(Main.thing1)
print('self.x' in lines)
The above, would print True for the code in question. I cannot think of a use case where this would be useful and strongly discourage it, but it's good to know the capabilities of Python.

Related

Why is super() not behaving like I expected when assigning to a class variable of the base class?

I am attempting to experiment with classes so I can better understand what they do. I wanted to build a counter which records the number of instances of a class (MyClass):
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
class MyClass(ObjectCounter):
def __init__(self):
super().myclass_obj_count += 1 # AttributeError: 'super' object has no attribute 'myclass_obj_count'
m1 = MyClass()
m2 = MyClass()
m3 = MyClass()
print(ObjectCounter.myclass_obj_count)
Since that didn't work, I looked online for someone trying to do the same thing. Here is some code I found online. This works as expected, and I feel like I have a basic understanding of how this works. This is a better solution to the task I was attempting, but I'm not satisfied because I want to know how super() works.
class geeks:
counter = 0
def __init__(self):
geeks.counter += 1
g1 = geeks()
g2 = geeks()
g3 = geeks()
print(geeks.counter) # this gives an expected result
Therefore, I tried this instead:
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
def add_myclass(self):
self.myclass_obj_count += 1
class MyClass(ObjectCounter):
def __init__(self):
super().add_myclass()
my_class_1 = MyClass()
my_class_2 = MyClass()
my_class_3 = MyClass()
print(ObjectCounter.myclass_obj_count) # expected output: 3
Instead of getting the expected output of 3, I got an output of 0. Why is this happening?
First, be aware of the += operator; it's implementation is quite subtle:
a += b
becomes
a = a.__iadd__(b)
This perhaps strange definition allows python to support it even for immutable types (like strings).
Note what happens when used for a class variable that is referred to by the alias self
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
def add_myclass(self):
self.myclass_obj_count += 1
# effectively becomes:
# self.myclass_obj_count = self.myclass_obj_count.__iadd__(1)
This will introduce an instance variable of the same name, shadowing the class variable.
You don't even need the subclass to test this:
>>> x = ObjectCounter()
>>> x.add_myclass()
>>> x.add_myclass()
>>> x.add_myclass()
>>> x.myclass_obj_count
3
>>> ObjectCounter.myclass_obj_count
0
Referring to the class variable directly instead of using self fixes this
def add_myclass(self):
ObjectCounter.myclass_obj_count += 1
I'm hesitant to give definite answers of what happens under the hood when class variables, super() and assignments are used, other than it just doesn't work. Perhaps because it would be quite ambiguous of whether or not we are defining class variables or new instance variables.
super() won't let you assign to either;
class ObjectCounter:
myclass_obj_count = 0
def __init__(self):
self.x = 'test'
class MyClass(ObjectCounter):
def __init__(self):
super().__init__()
print(super().myclass_obj_count) # reading works just fine
print(type(super())) # this isn't actually exactly the same as "ObjectCounter"
super().myclass_obj_count = 123 # no good
super().x = 'foo' # also no good.
All in all, for any assignment to class variables you can use the class name itself.

How to get object attributes to update dynamically in Python

I'd like to create a class that has 2 input attributes and 1 output attribute such that whenever one of the input attributes are modified the output attribute is modified automatically
I've tried defining the attributes as instance variables within and outside the constructor function but in either case, after instantiating the object, the output attribute remains fixed at the value set at the moment of instantiation
class Example():
def __init__(self,n):
self.name=n
inA=1
inB=1
if inA==1 and inB==1:
outA=1
else:
outA=0
when instantiated outA is set to 1 as expected
but if I try to update:
object.inA=0
object.outA remains 1 whereas I need it to be updated to 0
Trying to avoid the use of functions if possible. New to python and OOP so sorry if this question is nonsensical or has an obvious answer
If you want instance attributes that depend on other instance attributes, properties are the way to go.
class Example:
def __init__(self, n):
self.name = n
self.inA = 1
self.inB = 1
#property
def outA(self):
return self.inA and self.inB
You access outA like a regular instance attribute, obj.outA.
>>> my_obj = Example("example")
>>> my_obj.outA
1
Changing the attributes inA and inB affect outA.
>>> my_obj.inA = 0
>>> my_obj.outA
0
You can create a function in the class and some other minor changes:
class Example():
def __init__(self,n):
self.name=n
self.inA=1
self.inB=1
def f(self):
if self.inA==1 and self.inB==1:
self.outA=1
else:
self.outA=0
To call it:
a = Example('foo')
a.inA = 0
a.f()
print(a.outA)
Output:
0
As you can see, taking out:
a.f()
line would make it give an error:
AttributeError: 'Example' object has no attribute 'outA'
Do you want it to return your output?
Expanding on U9-Forward's answer:
class Example():
def __init__(self,n):
self.name = n
self.inA = 1
self.inB = 1
def f(self):
return self.inA and self.inB

Calling a variable from a classmethod function to a normal method

I would like to call a variable from a classmethod to a different method inside the same class:
class A():
#classmethod
def b(cls):
cls.g = 5
def c(self):
if self.g < 1:
print("TestA")
else:
print("TestB")
When doing:
x = A()
x.c()
I get:
AttributeError: 'A' object has no attribute 'g'
I've read and searched for a similar case but haven't found one. Most deal with calling variables from the init method and that doesn't apply here.
If you don't run .b() beforehand, your .g doesn't exist,...at all.
Add an __init__ function to your class and declare .g there to make sure it exists at least.
You did not define g as a class attribute of the class A. This could be done this way:
class A():
g = 7
but then in your code you are treating g as instance (self.g) and class variable (cls.g) at the same time. While this works (self.g will refer to cls.g) it may be confusing.

Reuse a class's method in the main.py

I've searched a lot about how to reuse a method from a class in the main.py file. i got some similar and basic solutions but in my case is a bit different.
/lib/module.py
class Myclass:
def __init__(self, x):
self.thisX = x
def check(self):
if self.thisX == 2:
print("this is fine. going to print it")
self.printing()
# this method will use in this class and must use from the main.py
# the parameter "z" is gonna use only when the method will call from main.py
def printing(self, z):
if z == 1 :
print("we got ", z)
else:
print(self.x)
/main.py
from lib.module import Myclass
# this is how i use the check() method once in my main.py
Myclass(2).check()
# the Myclass() gets "2" only once at the beginning of the program...
# i don't wanna pass "2" to the Myclass() everytime that i wanna use the printing() method...
c = Myclass()
c.printing(1)
error
TypeError: __init__() missing 1 required positional argument: 'x'
testing:
if i don't use the def init(), everything will be fine. but the problem is i need to keep it
This line in main.py:
c = Myclass()
Calls this function:
class Myclass:
def __init__(self, x):
self.thisX = x
Every time you create an instance of Myclass it will call the __init__() function. You declared it to take 2 arguments: self and x. self is always passed implicitly because it's a class, but you need to give it an argument 'x'.
So you can change main.py to this for example:
c = Myclass(2) # here x = 2
c.printing(1)
Please read this for more information
Also, in general, class names are written in CapWords style so it's a good idea to call your class MyClass instead of Myclass
Edit:
Since you don't want to pass x to __init__() and you want to set x from main.py you can try something like this:
class Myclass:
x = 0
def check(self):
if self.x == 2:
print("x is 2")
from main.py you can do:
Myclass.x = 2; #this only needs to be done once
Myclass().check()
Output:
x is 2
I think #richflow 's answer hit the point. If some variable is to be shared by all instances of a class, it's logical to assign its value using Myclass.x = new_number. Then all instances of this class will know the change.
If you really want to optionally change x in the __init__ method of an instance, you can still do it. Combining with #richflow's codes, it can look like the following.
class Myclass:
x = 0
def __init__(self, x=None):
if x is not None:
Myclass.x = x
# other codes for initializiing the instance
def check(self):
if Myclass.x == 2:
print("this is fine. going to print it")
def printing(self, z=0):
if z == 1 :
print("we got ", z)
else:
print(Myclass.x)
I tried not to change too much from your codes. Your main.py should work correctly with this class definition. However, the design looks a bit weird to me. Probably that's because I didn't understand clearly what the check and printing methods are really doing, and what the argument z is really doing in printing methods. If you provides more insights, probably people can help you with a better design.

Python 3: Calling a class function inside of __init__

I have a little question about python 3.
I want to create a class, which is using a function from within of that class. Just like:
class Plus:
def __init__(self, x, y):
self.x = x
self.y = y
self.test()
def test(self):
return self.x + self.y
now I am doing something like
a = Plus(5,6)
print(a)
and python is giving me
<__main__.Plus object at 0x000000000295F748>
and not 11 as I want it. I know that I can get 11 by
a = Plus(5, 6).test()
print(a)
but that's not what I want. I want to call the class and getting the result without adding .test() to it.
Can you help me?
I would go for:
class Plus:
def __init__(self, x, y):
self.x = x
self.y = y
self.test()
def test(self):
res = self.x + self.y
self.__repr__ = lambda:str(res)
return res
>>> Plus(5,5)
10
>>> a = Plus(5,5)
>>> a
10
>>> a.test()
10
This way you are not recomputing the sum each time you call print, its updated when you call the test method.
You'd need to define a __str__ method for your Plus class:
class Plus:
def __init__(self, x, y):
self.x = x
self.y = y
def test(self):
return self.x + self.y
def __str__(self):
return str(self.test())
now I am doing something like
a = Plus(5,6)
print(a)
and python is giving me
<__main__.Plus object at 0x000000000295F748>
and not 11 as I want it. I know that I can get 11 by
a = Plus(5, 6).test()
print(a)
but that's not what I want. I want to call the class and getting the result without adding .test() to it.
I am not sure what do you mean by 'and not 11 as I want it'. If you want Plus(5, 6) to actually return 11 (int instance), you should make Plus a function that returns the sum. Alternatively you can override __new__ method and hook upon object creation -- but this is a bad idea.
What are you trying to achieve?
I doubt, that by 'and not 11 as I want it' you want something special to be printed (formatted, represented). If so, override __str__ or __unicode__ or __repr__ method.
Edit:
ignore this answer, it is a comment on a misinterpretation of the question
This is just wrong.
when you instantiate an object, you'd expect to get a reference to that object.
if you just want a global function returning a number, why even bother to make a class with an init?
in python you shouldn't want static class's like in C# for encapsulation. instead name the module something, and use that for encapsulation.

Categories