I am facing problems with calling a variable outside of a subfunction:
class ABC():
def ___init___(self):
function_A()
function_B()
def function_A(self):
self.A = 5
def subfunction_of_A(self):
self.B = 2
self.function_B()
subfunction_of_A()
def function_B(self):
C = self.B
Start = ABC()
I always geht the error:
'ABC' object has no attribute 'B' for C = self.B
How can I make self.B accessible from outside?
Thanks a lot :)
------- EDIT/UPDATE ----------
Okay, I think i might need to update my question a little bit:
class ABC():
def ___init___(self):
self.function_A()
self.function_B()
def function_A(self):
self.A = 5
def subfunction_of_A(self):
self.B = 2
subfunction_of_A(self)
print(self.B) # This prints 2 and works as it should!
def function_B(self):
C = self.B # In this line I receive the error that ABC.B does not exist --> Why is that?
Start = ABC()
edit:
class ABC():
def __init__(self):
self.function_A()
self.function_B()
def function_A(self):
self.A = 5
def subfunction_of_A(self):
self.B = 2
subfunction_of_A(self)
def function_B(self):
print(self.B) # This prints 2 and works as it should!
C = self.B
Start = ABC()
this time your problem seems to be that your ___init___ has 3 underscores instead of 2... __init__
previous answer:
you're never calling your "sub function"
class ABC():
def function_A(self):
self.A = 5
def subfunction_of_A(self):
self.B = 2
subfunction_of_A(self) # notice this line
def function_B(self):
self.C = self.B
abc = ABC()
abc.function_A()
abc.function_B()
print(abc.C) # prints 2
the only way for B to be set is for that function to run even if its nested...its a weird way to set up a class but there you go
Related
I would like to reliably call the __del__ method of an object immediately when it gets deleted. I am aware that this has been asked for python 2, but the documentation of python 3 claims that the __del__ method is called when the reference count reaches 0. However, this does not seem to be the case with my program
import time
class MyClass:
object_list = []
def __init__(self):
MyClass.object_list.append(self)
print("object initialized")
def __del__(self):
print("executing cleanup steps")
MyClass.object_list.remove(self)
a = MyClass()
b = MyClass()
c = MyClass()
print(len(MyClass.object_list))
del b
time.sleep(1)
print("sleep finished")
print(len(MyClass.object_list))
Try it online
What I want to achieve is to remove b from the object_list immediately when it is destroyed and also make the variable b inaccessible. So simply calling b.__del__() is not enough, I would need to also render b inaccessible.
Note: running first b.__del__() and then del b will lead to complaints when exiting the python interpreter.
Interestingly enough, a very similar program seems to call b.__del__() immediately when del b is executed.
import time
class MyClass:
counter = 0
def __init__(self, value):
self.value = value
print("object initialized")
MyClass.counter+=1
def __del__(self):
print("executing cleanup steps")
MyClass.counter-=1
a = MyClass(5)
b = MyClass(6)
c = MyClass(7)
print(MyClass.counter)
del b
time.sleep(1)
print("sleep finished")
print(MyClass.counter)
Try it online
EDIT
#Pranav Hosangadi pointed out that there is still a reference to the object in the object_list. What a shameful oversight of mine. Now I came up with a workaround by calling first b.__del__() and then del b, but it is a two step process. I would like to have it in one step, if possible.
import time
class MyClass:
object_list = []
def __init__(self, value):
self.value = value
MyClass.object_list.append(self)
print("object initialized")
def __del__(self):
print("executing cleanup steps")
try:
MyClass.object_list.remove(self)
except:
pass
a = MyClass(1)
b = MyClass(2)
c = MyClass(3)
print(len(MyClass.object_list))
b.__del__()
del b
time.sleep(1)
print("sleep finished")
print(len(MyClass.object_list))
So I have this class:
class UniversalHash(HashClass):
##################################################
def __init__(self):
super().__init__()
self.__MParamK = int(0)
self.__MParamC = int(0)
self.__MParamD = int(0)
# Override #
def FindHash(self, Key):
return (((self.__MParamK * Key) + self.__MParamC) % self.__MParamD) % self.__MParamL
def SetParamK(self, Value):
self.__MParamK = Value
def SetParamC(self, Value):
self.__MParamC = Value
def SetParamD(self, Value):
self.__MParamD = Value
And the parent class:
class HashClass:
##################################################
def __init__(self):
self.__MParamL = int(0)
def SetParamL(self, Value):
self.__MParamL = Value
def GetParamL(self):
return self.__MParamL
def FindHash(self, Key):
pass
When I try to access to the variable __MParamL (the variable created in the parent), it gives me an exception telling me that the variable is not an attribute of this class, I have searched on the web and it seems this is the correct way to write the code (maybe the overridden function is the problem?). Any help is appreciated
When you name an instance attribute with a leading double underscore, it will get name mangled, E.g.,
>>> class A:
... def __init__(self):
... self.x = 42
... self.__y = 42
...
>>> a = A()
>>> vars(a)
{'x': 42, '_A__y': 42}
Instead, you should just use a single underscore, E.g.,
>>> class A:
... def __init__(self):
... self.x = 42
... self._y = 42
...
>>> a = A()
>>> vars(a)
{'x': 42, '_y': 42}
I very puzzled by the fact that __setattr__ is being called in an attempt to set a class attribute, which has already been set to an instance of another class. Consider the following code:
class A:
def __init__(self):
self.a = 42
def __add__(self, value):
print("Incrementing a of A by {}".format(value))
self.a += value
class B:
def __init__(self):
self.a = A()
self.b = 10
def __setattr__(self, attr, value):
print("Setting {} of B to {}".format(attr, value))
super(B, self).__setattr__(attr, value)
b = B()
print(b.b)
print(b.a)
b.b = 11
b.a += 1
print(b.b)
print(b.a)
When run, the code above produces the following output:
Setting a of B to <__main__.A object at 0x7f5e25438410>
Setting b of B to 10
10
<__main__.A object at 0x7f5e25438410>
Setting b of B to 11
Incrementing a of A by 1
Setting a of B to None
11
None
Obviously, b.a is correctly looked up and incremented. However, after the successful lookup, Python is attempting to create a new attribute of b called a. Why is that happending?
Answering my own question based on Jan Willems' comment:
Having __add__(self, value) return self fixes the described issue despite not being obvious. The documentation for the method (__add__ Documentation ) does not mention that __add__ should return anything when it is successful, which is where my confusion stemmed from.
I'm having trouble understanding how to instantiate a class, and update that instances variables. If I __init__ a series of self.x variables, then instance that class, I want to update self.x = 40. However, self.x always stays constant.
I have a feeling I'm not wrapping my head around the class variable, init variable, and instanced class variables. I can always access them, I just can't seem to change them. I have coded an example of what I am trying to do.
class Engine(object):
def __init__(self, board):
self.board = board
def play_game(self):
print(self.board.sheet[0])
number_one = int(input("Please enter a number."))
self.board.a = number_one
number_two = int(input("Please enter another number."))
self.board.b = number_two
number_three = int(input("Please enter a third number."))
self.board.c = number_three
number_four = int(input("Please enter a final number."))
self.board.d = number_four
print("Thank you! List updated.")
print(self.board.sheet[0])
class ScoreBoard(object):
def __init__(self):
self.a = "_____"
self.b = "_____"
self.c = "_____"
self.d = "_____"
self.sheet = [f"""
1. Number One: {self.a}
2. Number Two: {self.b}
3. Number Three: {self.c}
4. Number Four: {self.d}
"""]
new_board = ScoreBoard()
new_game = Engine(new_board)
new_game.play_game()
When I print self.board.sheet[0] I would like to show the numbers instead of the lines for self.a through self.d.
You need to recompute self.sheet after self.a through self.d are set. After self.sheet is assigned it just contains a simple string. That string isn't automatically updated when the fields are changed; you have to do it yourself.
Better yet, make sheet a method rather than a static variable.
class ScoreBoard(object):
def __init__(self):
self.a = "_____"
self.b = "_____"
self.c = "_____"
self.d = "_____"
def sheet(self):
return f"""
1. Number One: {self.a}
2. Number Two: {self.b}
3. Number Three: {self.c}
4. Number Four: {self.d}
"""
First, let's consider this working example using get and set methods for the variable x
class Foo:
def __init__(self):
self._x = 0
def set_x(self, x):
self._x = x
def get_x(self):
return self._x
class Bar:
def __init__(self, set_method):
self._set_method = set_method
def set_x(self, x):
self._set_method(x)
f = Foo()
f.set_x(5)
print(f.get_x())
# Prints 5
b = Bar(f.set_x)
b.set_x(10)
print(f.get_x())
# Prints 10
As you can see I pass the possibility to set the variable x of the instance f of class Foo, to the instance b of class Bar.
Now, I would like to do the same, but with property decorators instead, roughly like this
class Foo:
def __init__(self):
self._x = 0
#property
def x(self):
return self._x
#x.setter
def x(self, x):
self._x = x
class Bar:
def __init__(self, x_property):
self._x_property = x_property
def set_x(self, x):
self.x_property = x
f = Foo()
f.x = 5
print(f.x)
# Prints 5
b = Bar(f.x)
b.set_x(10)
print(f.x)
# Prints 5
What happens is that the value 5, instead of the property, gets passed to instance b, meaning that b can't access x in instance f. Is there a nice way to solve this?
I would then also like to do the same thing for the get method. In the first code that requires me to pass both methods, but if there is a way to get the second code to work I would hopefully only have to pass on the property which I then can set and get as a normal variable.
I would really want to use the property decorators or similar as it cleans up my code a lot. I use python 3.5.2.
Thanks,
Andreas
You can accomplish this by accessing the fset attribute of Foo.x. Note the use of class-dot notation rather than instance-dot. fset takes two arguments: the instance to access and the value to write. Here is a working example
class Foo:
#property
def x(self):
return self._x
#x.setter
def x(self, x):
self._x = x
class Bar:
def __init__(self, x_property):
self.x_property = x_property
def set_x(self, foo, value):
self.x_property(foo, value)
f = Foo()
f.x = 5
print(f.x)
b = Bar(Foo.x.fset)
b.set_x(f, 10)
print(f.x)
Notice that we had to pass f to set_x because we need it to invoke the setter. You could eliminate the f param by using partial from the functools module to bind f to the property setter. Pass the partial binding in to the constructor of Bar.
class Bar:
def __init__(self, x_property):
self.x_property = x_property
def set_x(self, value):
self.x_property(value)
f = Foo()
b = Bar(partial(Foo.x.fset, f))
b.set_x(10)
print(f.x)
It might be wise to rename x_property and this point. It is really just a function as far as Bar is concerned. It wouldn't have to be a property.