delete object as attribute from function - python-3.x

I want to delete an object using a function.
class Test():
def __init__(self, x):
self.x = x
foo = Test(5)
def delete(obj):
del obj
delete(foo)
print(foo)
In this code here, I am expecting it to give me an error on the last print statement, but it is printing my object, which I don't want. I want it to be deleted.
How would I go about doing so?

I will bite.
del obj deletes the locally-available reference to obj inside delete.
You will see it if you add any reference to obj inside delete:
def delete(obj):
del obj
obj
This causes the expected UnboundLocalError when calling delete.
Instead of del obj you can delete obj from the global namespace but you will have to use the name of the reference:
def delete(obj_name):
del globals()[obj_name]
then
class Test():
def __init__(self, x):
self.x = x
foo = Test(5)
def delete(obj_name):
del globals()[obj_name]
delete('foo')
print(foo)
NameError: name 'foo' is not defined
BUT Is there another goal you didn't explain? This on itself is a bit of a weird problem to be wanting to solve (ie XY problem). Why not let Python's GC handle the deletion of objects?

Related

parameters inside f-string in a dictionary cannot update later

I encounter this issue, when I instantiate an Test object and call the main() method, it prints out
https://bunch/of/urls/with/None/
Although I am expecting to have
https://bunch/of/urls/with/1234/
It cannot update the self.id inside the dictionary f-string later inside the main function.
My expectation is when the dictionary value retrieves given the key, it will update the self.id as well while returning the value.
The reason I am expecting that because I call self.b_func(1234) before I call self.a_func() so it updates the self.id.
However, it is not updating the self.id inside the f-string. This behavior I don't understand why. What concept am I missing? How can I fix this?
class Test():
def __init__(self,id=None):
self.id = id
# Problem happens here
self.a_dict = {'a' : f'https://bunch/of/urls/with/{self.id}/'}
def a_func(self):
val = self.a_dict['a']
print(val)
# It prints
# https://bunch/of/urls/with/None/
# Not
# https://bunch/of/urls/with/1234/
def b_func(self, id):
self.id = id
return True
def main(self):
self.b_func(1234)
self.a_func()
if __name__ == '__main__':
a = Test()
a.main()
f-strings are evaluated right where they are defined. They do not magically update themselves when the values of the expressions inside change afterwards.
You can consider making a_dict a property instead so that its value would be dynamically generated:
class Test():
def __init__(self,id=None):
self.id = id
#property
def a_dict(self):
return {'a' : f'https://bunch/of/urls/with/{self.id}/'}
Demo: https://replit.com/#blhsing/UnnaturalElasticClasslibrary

How to pass self to function instance when it gets assigned in a decorator?

I am trying to assign dictionary keys to object functions but for some reason it won't work inside of decorators. When I try to call a.run(), self doesn't seem to be passed into the dictionary func. I also don't have access to f.self in decorator so I know it has to be something wrong in there. I have written a simple example of my code. I want it to be something similar to app.route in flask being that it init the mapping between endpoints and functions.
ERROR:
Traceback (most recent call last):
File "main.py", line 27, in <module>
a.run()
File "main.py", line 14, in run
self.rmap[k](data)
TypeError: one_way() missing 1 required positional argument: 'data'
CODE:
class A (object):
def __init__(self):
self.rmap = {}
def route(self, r):
def decorator(f):
self.rmap[r] = f
return f
return decorator
def run(self):
data = [1,2,3]
for k in self.rmap.keys():
self.rmap[k](data)
a = A()
class B (object):
def __init__(self):
pass
#a.route('/one/way')
def one_way (self, data):
print('A WAY:{}'.format(self))
b = B()
a.run()
At the time it's being decorated, one_way() is a plain function, not a method - it only becomes a method when looked up on a B instance. IOW, you need to explicitely provide a B instance when calling it from A().run() (the fact you have a global b instance in your code is irrelevant - the function object stored in a.rmap knows absolutely nothing about it, nor even about the B class FWIW.
To make a long story short, your current design cannot work as is. If you only ever intend to decorate methods (well, functions) from one single class and call them on one single instance of this class, you could pass an instance of this class to a.run() ie:
class A():
# ...
def run(self, obj):
data = [1,2,3]
for k in self.rmap.keys():
self.rmap[k](obj, data)
b = B()
a.run(b)
but this would be of very limited use.
Or you could just use the decorator to "mark" functions to be used for routing (together with the effective route), add some register() methdo to A and explicitely pass B or whatever else instance to this method ie
def route(r):
def decorator(f):
f._A_route = r
return f
return decorator
class A (object):
def __init__(self):
self.rmap = {}
def register(self, *objects):
for obj in objects:
self._register(obj)
def _register(self, obj):
for name in dir(obj):
if name.startswith("_"):
continue
attr = getattr(obj, name)
if callable(attr) and hasattr(attr, "_A_route"):
self.rmap[attr._A_route] = attr
def run(self):
data = [1,2,3]
for k in self.rmap.keys():
self.rmap[k](data)
class B (object):
def __init__(self):
pass
#route('/one/way')
def one_way (self, data):
print('A WAY:{}'.format(self))
if __name__ == "__main__":
a = A()
b = B()
a.register(b)
a.run()
Now there might be better solutions for your concrete use case, but it's impossible to tell without knowing about the whole context etc.
When calling self.rmap[k](data) you are not passing in the self parameter. This has to be an instance of class B in order to work.
Normally you'd just pass on the parameters with which the decorated function was called, but you seem to want to use your decorated function differently. In your case what would work is:
def run(self):
data = [1,2,3]
b = B()
for k in self.rmap.keys():
self.rmap[k](b, data)
You could of course also instantiate the B instance somewhere else if you want to reuse it between calls.

Can we skip explicit object creation in Python

When I do not crate object for CP class, the operations are not captured. I am referring to the code below, Can somebody help me understand why we need obj creation in this case
from abc import ABC, abstractmethod
class P(ABC):
def __init__(self):
super().__init__()
self._pre_map = {}
self._pre_order = []
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
def execute(self):
pass
class CP(P):
def __init__(self):
super().__init__()
def execute(self):
self.prnt()
def prnt(self):
print (self._pre_map)
print (self._pre_order)
#Working
print("\n++++++++ working")
obj = CP()
obj.set_pre("test string added")
obj.execute()
#Not Working
print("\n+++++++ not working")
CP().set_pre("test string added")
CP().execute()
It produces,
++++++++working
0
{0: 'test string added'}
[0]
+++++++not working
0
{}
[]
When you call the class the second time with CP.execute(), you have created a completely new instance of the CP class. It is not going to have the text string you specified.
If you actually wanted it to print the values like the working one you can make the functions return self after each call in the P class. If you did that you could do something like this.
from abc import ABC, abstractmethod
class P(ABC):
def __init__(self):
super().__init__()
self._pre_map = {}
self._pre_order = []
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
##need to return self here
return self
def execute(self):
pass
class CP(P):
def __init__(self):
super().__init__()
def execute(self):
self.prnt()
def prnt(self):
print (self._pre_map)
print (self._pre_order)
#Working
print("\n++++++++ working")
obj = CP()
obj.set_pre("test string added")
obj.execute()
#Not Working
print("\n+++++++ not working: but now working after returning self in the P class")
CP().set_pre("test string added").execute()
++++++++ working
0
{0: 'test string added'}
[0]
+++++++ not working: but now working after returning self in the P class
0
{0: 'test string added'}
[0]
This would print the result you want.
The reason for the difference is the fact that in the first one, you are creating an instance, and using that instance the whole way through, whereas in the second one, you are using two different instances of your class.
The two different instances cannot share their attributes, so you are unable to recall what happened. If you really don't want to use a dedicated variable, change your P class to look like this:
class P(ABC):
...
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
return self
...
And use CP().set_pre("test string added").execute()

Nested Function in a Class, Python

I am trying to have Nested function inside a class. Here is my coad
class big():
def __init__(self):
self.mas = "hello"
def update(self):
def output(self):
print(self.mas)
self.output()
thing = big()
thing.update()
However when it runs I get an error that output is not defined. How can i run the output function inside the update function?
Just call it as output(), without self. The way you've defined it, it basically is a local variable inside your update method, not an attribute of the class.
class big():
def __init__(self):
self.mas = "hello"
def update(self):
def output():
print(self.mas)
output()
thing = big()
thing.update()

Could you fix the Python init function?

I have this Python code...
class clss:
def __init__(self, d):
self.data = d
But every time I run it, I get this error...
AttributeError: 'clss' object has no attribute 'data'
How do I fix the error?
Your variable data is not defined anywhere, hence it cannot be assigned to self.data.
You'll want to pass data as an argument to __init__ instead of d:
class clss:
def __init__(self, data):
self.data = data

Resources