Reputation: 954
I have a StatsVar
class to maintain the statistic variable, and a Ctx
class to register the statistic variables as its attributes as follows.
class StatsVar:
def __init__(self, v):
self.v = v
class Ctx:
def __init__(self):
self.vars = list()
def register(self, v):
self.vars.append(v)
Create a StatsVars
and register into an instance of Ctx
by the following code:
ctx = Ctx()
var = StatsVar(0.)
ctx.register(var)
What I want to do:
StatsVar
into ctx
(Rather than Ctx
)However, I don't know how to obtain the instance of Ctx
(like self
) to run self.vars.append(v)
in the following code:
@Ctx.register
class StatsVar:
def __init__(self, v):
self.v = v
class Ctx:
def __init__(self):
self.vars = list()
def register(cls):
def wrapper(v):
# !!! HERE, I know it's wrong, but how to obtain `self`?
self.vars.append(v)
return cls(v)
return wrapper
Update
I found a feasible solution is to place StatsVar
as an inner class and the creation of StatsVar
is left to the instance of Ctx
as follows:
class Ctx:
def __init__(self):
self.vars = list()
def statsvar(self):
manager = self
class StatsVar:
def __init__(self, v):
self.v = v
manager.vars.append(v)
return StatsVar
if __name__ == '__main__':
ctx = Ctx()
ctx.statsvar()(0.)
print('')
However, calling ctx.statsvar()(0.)
seems terrible and is there any better solution here?
Upvotes: 2
Views: 126
Reputation: 4988
Thinking in terms of sender and receiver, respectively StatsVar
and Ctx
, each instance of the sender "fires" a signal to the listener class. To do that, a function decorator can be used to intercept the sender-set_v
and update the receiver-set_vars
.
def send_register_signal(receiver):
def wrapper(sender):
setattr(sender, 'set_v', lambda self, v: (receiver.set_vars(v), setattr(self, 'v', v)))
return sender
return wrapper
class Ctx:
vars = []
@classmethod
def set_vars(cls, v): cls.vars.append(v)
@classmethod
def get_vars(cls): return cls.vars
@send_register_signal(Ctx)
class StatsVar:
def __init__(self, v): self.set_v(v)
def set_v(self, v): self.v = v
def get_v(self): return self.v
# test
a = StatsVar(3)
b = StatsVar('smt')
c = StatsVar(7)
print(Ctx.vars)
print(b.v)
print(c.v)
Output
[3, 'smt', 7]
smt
7
EDIT
To be more consistent with the original question the decorator can be implemented directly in the receiver class.
class Ctx:
vars = []
@classmethod
def set_vars(cls, v): cls.vars.append(v)
@classmethod
def get_vars(cls): return cls.vars
@classmethod
def register(cls, sender):
setattr(sender, 'set_v', lambda self, v: (cls.set_vars(v), setattr(self, 'v', v)))
return sender
@Ctx.register
class StatsVar:
....
Upvotes: 1
Reputation: 9047
This can be a bad idea but you can use a singleton class like this below--
class Ctx:
_instance = None
vars = list()
def __new__(cls, *args, **kwargs):
if not isinstance(cls._instance, cls):
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
def register(self, v):
self.vars.append(v)
class StatsVar():
def __init__(self,v):
self.v = v
Ctx().register(self)
def __repr__(self):
return f'{self.v}'
ctx = Ctx()
StatsVar(0)
StatsVar(1)
StatsVar(2)
print(ctx.vars) # [0, 1, 2]
Upvotes: 0