Reputation: 331
I want to create a one time usage method in my class. It should be possible to call the function one time, use a variable of the class, remove the variable and also remove the function. If the method is called afterwards it should raise an error that this method does not exist. If created a workaround which kind of achieves what I want, but the traceback is still not perfect, since the method still exists but raises the correct error.
class MyClass:
def __init__(self):
self.a = 1
def foo(self):
if hasattr(self, 'a'):
delattr(self, 'a')
else:
raise AttributeError('foo')
This gives
Test = MyClass()
Test.foo()
Test.foo()
output:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/Users/path in <cell line: 3>()
1 Test = MyClass()
2 Test.foo()
----> 3 Test.foo()
/Users/path in MyClass.foo(self)
6 delattr(self, 'a')
7 else:
----> 8 raise AttributeError('foo')
AttributeError: foo
This is the closest I got.
Edit:
Use cas:
The use case for this is a bit complicated, since it is completly out of context. But the idea is that I initialize a lot of these classes and need a one time read out of some information directly afterwards.
More specific, the class is splitting data in a specific way via the __getitem__
method which is most efficient. But I still need the full index list used, which should be the output of the one time function.
I know this is not necessary, I could just never use the method again and everything is fine, but it feels odd having a method that is actually not working anymore. So I am just interested if this is somewhat possible.
Upvotes: 1
Views: 202
Reputation: 178
I want to start by saying that I highly recommend you not to implement something like that. Basically, it's creating code with state which could only be discovered at runtime. Although python allows us to do it, usually it is prune to future bugs and makes developer experience in the future harder. For example, any changes done in runtime are not available for your IDE to discover. So while you can do something that does work, it will be hard to understand for the common developer that tries to continue your project. A collection of a lot of things like that can even kill a project and make it unreliable.
If that's really what you want to do, you can override __dir__
on the class to show what fields are available.
For example you can write something like that:
class MyClass:
def __init__(self):
self._was_foo_called = False
def foo(self):
if not self._was_foo_called:
self._was_foo_called = True # to make sure it won't be called again
# do logic
def __dir__(self):
super_dir = super(MyClass, self).__dir__()
if self._was_foo_called:
super_dir.remove('foo')
return super_dir
Then, when running your code from ipython
for example, the first call for foo
will be auto completed, but the second one won't.
You should note that it is still on the class, just won't be auto completed.
If you want to remove it from the instance entirely you can override __dict__
instead.
Upvotes: 1
Reputation: 12140
I don't fully understand your use case, but if I were a developer who is using MyClass
, I'd be really unhappy to receive AttributeError
on the second call of foo
. And I'll be really happy to get a self-explanatory error:
class MyClass:
def __init__(self):
self._called = False
def foo(self):
if self._called:
raise Exception('foo can be called only once')
self._called = True
# the logic
And here is the solution to your exact question (wouldn't recommend it):
class MyClass:
def __init__(self):
def foo():
# logic
print('fooing')
del self.foo
self.foo = foo
>>> t1 = MyClass()
>>> t2 = MyClass()
>>> t1.foo()
fooing
>>> t1.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'foo'
>>> t2.foo()
fooing
>>> t2.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'foo'
Upvotes: 4
Reputation: 197
I used the del keyword to remove the function like this:
class MyClass:
def __init__(self):
self.a = 1
def foo(self):
# do something...
if hasattr(self, 'a'):
delattr(self, 'a')
del MyClass.foo
my_obj = MyClass()
my_obj.foo()
my_obj.foo()
The output is this:
Traceback (most recent call last):
File "/path/tmp.py", line 15, in <module>
my_obj.foo()
^^^^^^^^^^
AttributeError: 'MyClass' object has no attribute 'foo'
I got it from here.
Upvotes: 1