Context
class MyMockClient
below has two methods:
push_log
: pushes onelog
from listself.logs
and pops itpush_event
: pushes oneevent
from listself.events
and pops it
Both methods are decorated using __iterate_pushes
which essentially
iterate push_event and push_log until their respective lists are entirely popped
AND in the full program applies some extra logics (e.g. tracks how many objects were pushed).
Problem
The decorator __iterate_pushes
needs to access self.logs
and self.events
.
However, it does not seem to work. In the example below, you can see that the program fails because self.logs is not available when the function is defined.
Any idea how to solve this?
Example
class MyMockClient:
def __init__(self, logs, events):
self.logs = logs
self.events = events
def __iterate_pushes(self, obj):
"""
Decorate function to iterate the 'pushes' until
the lists are entirely popped.
"""
def outer_wrapper(self, decorated_func):
def inner_wrapper(self):
responses = []
print(f"len: {len(obj)}")
while len(obj) > 0: # iterate until list is entirely popped
response = decorated_func() # when executed, it pops the first item
responses.append(response)
print(f"len: {len(obj)}")
return responses
return inner_wrapper
return outer_wrapper
@__iterate_pushes(obj = self.logs)
def push_log(self):
# Send first log and pops it if successful
log = self.logs[0]
print(log, "some extra logics for logs")
response = "OK" # let's assume API call is OK
if response == "OK":
self.logs.pop(0)
return response
@__iterate_pushes(obj = self.events)
def push_event(self):
# Send first event and pops it if successful
event = self.events[0]
print(event, "some extra logics for events")
response = "OK" # let's assume API call is OK
if response == "OK":
self.events.pop(0)
return response
my_mock_client = MyMockClient(
logs = [1, 2, 3],
events = [4, 5, 6]
)
my_mock_client.push_log()
Error
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[2], line 1
----> 1 class MyMockClient:
2 def __init__(self, logs, events):
3 self.logs = logs
Cell In[2], line 25, in MyMockClient()
21 return inner_wrapper
23 return outer_wrapper
---> 25 @__iterate_pushes(obj = self.logs)
26 def push_log(self):
27 # Send first log and pops it if successful
28 log = self.logs[0]
29 print(log, "some extra logics for logs")
NameError: name 'self' is not defined
CodePudding user response:
This is my opinion, but I believe you are using decorator when it shouldn't be used. This is not intuitive and it implicitly changes the logic and signature of the decorated methods (e.x. instead of returning a single response
it makes the method return a list of response
s). I'd suggest making a simple "private" method instead that you'd call from inside of push_log
and push_event
:
class MyMockClient:
def __init__(self, logs, events):
self.logs = logs
self.events = events
def _iterate_pushes(self, obj):
responses = []
print(f"len: {len(obj)}")
while len(obj) > 0: # iterate until list is entirely popped
response = obj.pop(0) # when executed, it pops the first item
responses.append(response)
print(f"len: {len(obj)}")
return responses
def push_log(self):
# Send first log and pops it if successful
log = self.logs[0]
print(log, "some extra logics for logs")
return self._iterate_pushes(self.logs)
def push_event(self):
# Send first event and pops it if successful
event = self.events[0]
print(event, "some extra logics for events")
return self._iterate_pushes(self.events)
Answer to your question (don't recommend):
Use getattr
and pass a field name:
class MyMockClient:
...
def __iterate_pushes(self, obj_name):
"""
Decorate function to iterate the 'pushes' until
the lists are entirely popped.
"""
def outer_wrapper(self, decorated_func):
def inner_wrapper(self):
responses = []
obj = getattr(self, obj_name)
...
return responses
return inner_wrapper
return outer_wrapper
@__iterate_pushes(obj_name='logs')
def push_log(self):
...
return response
@__iterate_pushes(obj_name='events')
def push_event(self):
...
return response