Hi,
after hearing a lot about decorators and never actually using one I have
decided to give it a try. My particular usecase is that I have class that
acts as a proxy to other classes (i.e. passes messages along to those
classes) however hand-coding this type of class is rather tedious, so I
decided to use decorator for that. Can somebody tell me if what I'm doing
is a potential shot-in-the-foot or am I on the right track? (Note, It's
rather rudimentary proof-of-concept implementation and not the final
solution I'm about to employ so there are no optimizations or
signature-preserving code there yet, just the idea).
Here's the code:
class A:
b=None
def __init__(self,b ):
self.val='aval'
self.b=b
b.val='aval'
def mymethod(self,a ):
print "A::mymetho d, ",a
def mymethod2(self, a):
print "A::another method, ",a
def Aproxy(fn):
def delegate(*args, **kw):
print "%s::%s" % (args[0].__class__.__na me__,fn.__name_ _)
args=list(args)
b=getattr(args[0],'b')
fnew=getattr(b, fn.__name__)
# get rid of original object reference
del args[0]
fnew(*args,**kw )
setattr(A,fn.__ name__,delegate )
return fn
class B:
def __init__(self):
self.val='bval'
@Aproxy
def bmethod(self,a) :
print "B::bmethod "
print a, self.val
@Aproxy
def bmethod2(self,a ):
print "B::bmethod 2"
print a, self.val
b=B()
b.bmethod('foo' )
a=A(b)
b=B()
b.val='newval'
a.bmethod('bar' )
a.bmethod2('zam ') 25 1454
On Sep 24, 3:21*pm, "Dmitry S. Makovey" <dmi...@athabas cau.cawrote:
Hi,
after hearing a lot about decorators and never actually using one I have
decided to give it a try. My particular usecase is that I have class that
acts as a proxy to other classes (i.e. passes messages along to those
classes) however hand-coding this type of class is rather tedious, so I
decided to use decorator for that. Can somebody tell me if what I'm doing
is a potential shot-in-the-foot or am I on the right track? (Note, It's
rather rudimentary proof-of-concept implementation and not the final
solution I'm about to employ so there are no optimizations or
signature-preserving code there yet, just the idea).
Your code below is very abstract, so it's kind of hard to figure out
what problem you're trying to solve, but it seems to me that you're
using the B proxy class to decorate the A target class, which means
you want one of these options:
1) Put decorators over the methods in A, not B. Isn't it the
methods of A that are being decorated here?
2) Eliminate the decorator syntax and make your code more
expressive:
a = SomeClass()
# first call it directly
x = a.foo()
y = a.bar()
# now decorate it
debug_proxy =
ClassThatDecora tesMethodCallsT oObjectWithDebu ggingCode(a)
debug_proxy.dec orate_methods(' foo', 'bar')
The decorate_method s method would be magical, in terms of overwriting
a's innards, while still preserving the same interface for its users.
But again, I'm just guessing here, because it's hard to know what
problem you're really solving.
Cheers,
Steve
Code quoted below:
Here's the code:
class A:
* * b=None
* * def __init__(self,b ):
* * * * self.val='aval'
* * * * self.b=b
* * * * b.val='aval'
* * def mymethod(self,a ):
* * * * print "A::mymetho d, ",a
* * def mymethod2(self, a):
* * * * print "A::another method, ",a
def Aproxy(fn):
* * def delegate(*args, **kw):
* * * * print "%s::%s" % (args[0].__class__.__na me__,fn.__name_ _)
* * * * args=list(args)
* * * * b=getattr(args[0],'b')
* * * * fnew=getattr(b, fn.__name__)
* * * * # get rid of original object reference
* * * * del args[0]
* * * * fnew(*args,**kw )
* * setattr(A,fn.__ name__,delegate )
* * return fn
class B:
* * def __init__(self):
* * * * self.val='bval'
* * @Aproxy
* * def bmethod(self,a) :
* * * * print "B::bmethod "
* * * * print a, self.val
* * @Aproxy
* * def bmethod2(self,a ):
* * * * print "B::bmethod 2"
* * * * print a, self.val
b=B()
b.bmethod('foo' )
a=A(b)
b=B()
b.val='newval'
a.bmethod('bar' )
a.bmethod2('zam ')
sh************@ gmail.com wrote:
Your code below is very abstract, so it's kind of hard to figure out
what problem you're trying to solve, but it seems to me that you're
using the B proxy class to decorate the A target class, which means
you want one of these options:
Sorry for unclarities in original post. Basically A aggregates object of
class B (example with no decorators and again it's oversimplified) :
class A:
b=None
def __init__(self,b ):
self.b=b
def amethod(self,a) :
print "A::amethod ", a
def bmethod(self,a) :
print "A::bmethod ",a
return self.b.bmethod( a)
def bmethod2(self,a ,z):
print "A::bmethod 2 ",a,z
return self.b.bmethod2 (a,z)
class B:
def __init__(self):
self.val=a
def bmethod(self,a) :
print "B::bmethod ",a
def bmethod2(self,a ,z):
print "B::bmethod 2 ",a,z
b=B()
a=A(b)
a.bmethod('foo' )
a.bmethod2('bar ','baz')
In my real-life case A is a proxy to B, C and D instances/objects, not just
one. If you look at above code - whenever I write new method in either B, C
or D I have to modify A, or even when I modify signature (say, add
parameter x to bmethod) in B, C or D I have to make sure A is synchronized.
I was hoping to use decorator to do it automatically for me. Since the
resulting code is virtually all the same for all those proxy methods it
seems to be a good place for automation. Or am I wrong assuming that?
(since it is my first time using decorators I honestly don't know)
Abovementioned code ilustrates what I am doing right now. My original post
is an attempt to make things more automated/foolproof.
Dmitry S. Makovey wrote:
In my real-life case A is a proxy to B, C and D instances/objects, not
just one.
forgot to mention that above would mean that I need to have more than one
decorator function like AproxyB, AproxyC and AproxyD or make Aproxy smarter
about which property of A has instance of which class etc.
Unless I'm totally "out for lunch" and there are better ways of implementing
this (other than copy-pasting stuff whenever anything in B, C or D
changes).
Dmitry S. Makovey schrieb:
Dmitry S. Makovey wrote:
>In my real-life case A is a proxy to B, C and D instances/objects, not just one.
forgot to mention that above would mean that I need to have more than one
decorator function like AproxyB, AproxyC and AproxyD or make Aproxy smarter
about which property of A has instance of which class etc.
Unless I'm totally "out for lunch" and there are better ways of implementing
this (other than copy-pasting stuff whenever anything in B, C or D
changes).
__getattr__?
class Proxy(object):
def __init__(self, delegate):
self._delegate = delegate
def __getattr__(sel f, attr):
v = getattr(self._d elegate, attr)
if callable(v):
class CallInterceptor (object):
def __init__(self, f):
self._f = f
def __call__(self, *args, **kwargs):
print "Called " + str(self._f) + " with " +
str(args) + str(kwargs)
return self._f(*args, **kwargs)
return CallInterceptor (v)
return v
Decorators have *nothing* to do with this. They are syntactic sugar for
def foo(...):
...
foo = a_decorator(foo )
Nothing less, nothing more.
Diez
Diez B. Roggisch wrote:
Dmitry S. Makovey schrieb:
>Dmitry S. Makovey wrote:
>>In my real-life case A is a proxy to B, C and D instances/objects, not just one.
forgot to mention that above would mean that I need to have more than one decorator function like AproxyB, AproxyC and AproxyD or make Aproxy smarter about which property of A has instance of which class etc.
__getattr__?
see, in your code you're assuming that there's only 1 property ( 'b' )
inside of A that needs proxying. In reality I have several. So in your code
self._delegate should be at least a tupple or a list. Plus what you're
doing - you just promiscuously passing any method not found in Proxy to
self._delegate which is not what I need as I need to pass only a subset of
calls, so now your code needs to acquire dictionary of "allowed" calls, and
go over all self._delegates to find if any one has it which is not
efficient since there IS a 1:1 mapping of A::method -B::method so lookups
shouldn't be necessary IMO (for performance reasons).
class Proxy(object):
def __init__(self, delegate):
self._delegate = delegate
def __getattr__(sel f, attr):
v = getattr(self._d elegate, attr)
if callable(v):
class CallInterceptor (object):
def __init__(self, f):
self._f = f
def __call__(self, *args, **kwargs):
print "Called " + str(self._f) + " with " +
str(args) + str(kwargs)
return self._f(*args, **kwargs)
return CallInterceptor (v)
return v
Decorators have *nothing* to do with this. They are syntactic sugar for
def foo(...):
...
foo = a_decorator(foo )
exactly. and in my case they would've simplified code reading/maintenance.
However introduced "tight coupling" (A knows about B and be should know
about A) is something that worries me and I'm trying to figure out if there
is another way to use decorators for my scenario or is there another way of
achieving the same thing without using decorators and without bloating up
the code with alternative solution.
Another way could be to use Metaclass to populate class with method upon
declaration but that presents quite a bit of "special" cruft which is more
than I have to do with decorators :) (but maybe it's all *necessary* ? )
Thanks Bruno,
your comments were really helpful (so was the "improved" version of code).
My replies below:
Bruno Desthuilliers wrote:
>So decorators inside of B just identify that those methods will be proxied by A. On one hand from logical standpoint it's kind of weird to tell class that it is going to be proxied by another class,
Indeed - usually, proxied objects shouldn't have to be aware of the
fact. That doesn't mean your variation on the proxy pattern is
necessarily bad design (hard to tell without lot of context anyway...),
but still there's some alarm bell ringing here IMHO - IOW : possibly the
right thing to do, but needs to be double-checked.
I'm kind of looking at options and not dead-set on decorators, but I can't
find any other "elegant enough" solution which wouldn't lead to such tight
coupling. The problem I'm trying to solve is not much more complicated than
what I have already described so if anybody can suggest a better approach -
I'm all for it.
Now I'm not sure I really like your implementation. Here's a possible
rewrite using a custom descriptor:
yeah, that was going to be my next step - I was just aiming for
proof-of-concept more then efficient code :)
class Proxymaker(obje ct):
def __init__(self, attrname):
self.attrname = attrname
def __get__(self, instance, cls):
def _proxied(fn):
fn_name = fn.__name__
def delegate(inst, *args, **kw):
target = getattr(inst, self.attrname)
#return fn(target, *args,**kw)
method = getattr(target, fn_name)
return method(*args, **kw)
delegate.__name __ = "%s_%s_delegate " % \
(self.attrname, fn_name)
setattr(cls, fn_name, delegate)
return fn
return _proxied
class A(object):
def __init__(self,b ):
self.val='aval'
self.b=b
b.val='aval'
proxy2b = Proxymaker('b')
def mymethod(self,a ):
print "A::mymetho d, ",a
def mymethod2(self, a):
print "A::another method, ",a
class B(object):
def __init__(self):
self.val='bval'
@A.proxy2b
def bmethod(self,a) :
print "B::bmethod "
print a, self.val
@A.proxy2b
def bmethod2(self,a ):
print "B::bmethod 2"
print a, self.val
My point is that:
1/ you shouldn't have to rewrite a decorator function - with basically
the same code - for each possible proxy class / attribute name pair combo
2/ making the decorator an attribute of the proxy class makes
dependencies clearer (well, IMHO at least).
agreed on all points
I'm still a bit uneasy wrt/ high coupling between A and B, and if I was
to end up with such a design, I'd probably take some times to be sure
it's really ok.
that is the question that troubles me at this point - thus my original post
(read the subject line ;) ). I like the clarity decorators bring to the
code and the fact that it's a solution pretty much "out-of-the-box" without
need to create something really-really custom, but I'm worried about tight
coupling and somewhat backward logic that they would introduce (the way I
envisioned them).
Aaron "Castironpi " Brady wrote:
You should write it like this:
class B(object):
@A.proxy
def bmethod(self,a) :
Making 'proxy' a class method on A.
makes sense.
In case different A instances (do
you have more than one BTW?)
yep. I have multiple instances of class A, each one has properties (one per
class) of classes B, C and D:
class A:
b=None
c=None
d=None
def __init__(self,b ,c,d):
self.b=b
self.c=c
self.d=d
...magic with proxying methods goes here...
class B:
def bmethod(self,x) : pass # we proxy this method from A
def bmethod2(self,x ): pass # this is not proxied
class C:
def cmethod(self,x) : pass # we proxy this method from A
class D:
def dmethod(self,x) : pass # we proxy this method from A
a=A(B(),C(),D() )
x='foo'
a.bmethod(x)
a.cmethod(x)
a.dmethod(x)
a.bmethod2(x) # raises error as we shouldn't proxy bmethod2
above is the ideal scenario.
What you've said implies that you only have one B instance, or only
one per A instance. Is this correct?
yes. as per above code.
I agree that __setattr__ is the canonical solution to proxy, but you
have stated that you want each proxied method to be a member in the
proxy class.
well. kind of. if I can make it transparent to the consumer so that he
shouldn't do:
a.b.bmethod(x)
but rather:
a.bmethod(x)
As I'm trying to keep b, c and d as private properties and would like to
filter which calls are allowed to those. Plus proxied methods in either one
always expect certain parameters like:
class B:
def bmethod(self,c, x): pass
and A encapsulates 'c' already and can fill in that blank automagically:
class A:
c=None
b=None
def bmethod(self,c, x):
if not c:
c=self.c
b.bmethod(self, c,x)
I kept this part of the problem out of this discussion as I'm pretty sure I
can fill those in once I figure out the basic problem of auto-population of
proxy methods since for each class/method those are going to be nearly
identical. If I can autogenerate those on-the-fly I'm pretty sure I can add
some extra-logic to them as well including signature change where
A::bmethod(self ,c,x) would become A::bmethod(self ,x) etc.
On Sep 25, 1:22*pm, "Dmitry S. Makovey" <dmi...@athabas cau.cawrote:
Aaron "Castironpi " Brady wrote:
You should write it like this:
class B(object):
* * *@A.proxy
* * *def bmethod(self,a) :
Making 'proxy' a class method on A. *
makes sense.
In case different A instances (do
you have more than one BTW?)
yep. I have multiple instances of class A, each one has properties (one per
class) of classes B, C and D:
class A:
* * * * b=None
* * * * c=None
* * * * d=None
* * * * def __init__(self,b ,c,d):
* * * * * * * * self.b=b
* * * * * * * * self.c=c
* * * * * * * * self.d=d
* * * * ...magic with proxying methods goes here...
class B:
* * * * def bmethod(self,x) : pass # we proxy this method from A
* * * * def bmethod2(self,x ): pass # this is not proxied
class C:
* * * * def cmethod(self,x) : pass # we proxy this method from A
class D:
* * * * def dmethod(self,x) : pass # we proxy this method from A
a=A(B(),C(),D() )
x='foo'
a.bmethod(x)
a.cmethod(x)
a.dmethod(x)
a.bmethod2(x) # raises error as we shouldn't proxy bmethod2
above is the ideal scenario.
What you've said implies that you only have one B instance, or only
one per A instance. *Is this correct?
yes. as per above code.
I agree that __setattr__ is the canonical solution to proxy, but you
have stated that you want each proxied method to be a member in the
proxy class.
well. kind of. if I can make it transparent to the consumer so that he
shouldn't do:
a.b.bmethod(x)
but rather:
a.bmethod(x)
As I'm trying to keep b, c and d as private properties and would like to
filter which calls are allowed to those. Plus proxied methods in either one
always expect certain parameters like:
class B:
* * * * def bmethod(self,c, x): pass
and A encapsulates 'c' already and can fill in that blank automagically:
class A:
* * * * c=None
* * * * b=None
* * * * def bmethod(self,c, x):
* * * * * * * * if not c:
* * * * * * * * * * * * c=self.c
* * * * * * * * b.bmethod(self, c,x)
I kept this part of the problem out of this discussion as I'm pretty sureI
can fill those in once I figure out the basic problem of auto-population of
proxy methods since for each class/method those are going to be nearly
identical. If I can autogenerate those on-the-fly I'm pretty sure I can add
some extra-logic to them as well including signature change where
A::bmethod(self ,c,x) would become A::bmethod(self ,x) etc.
Do you want to couple instances or classes together?
If A always proxies for B, C, and D, then the wrapper solution isn't
bad. If you're going to be doing any instance magic, that can change
the solution a little bit.
There's also a revision of the first implementation of Aproxy you
posted, which could stand alone as you have it, or work as a
classmethod or staticmethod.
def Aproxy(fn):
def delegate(*args, **kw):
print "%s::%s" % (args[0].__class__.__na me__,fn.__name_ _)
args=list(args)
b=getattr(args[0],'b')
fnew=getattr(b, fn.__name__)
# get rid of original object reference
del args[0]
fnew(*args,**kw )
setattr(A,fn.__ name__,delegate )
return fn
def Aproxy(fn):
def delegate(self,* args,**kw):
print "%s::%s" % (args[0].__class__.__na me__,fn.__name_ _)
fnew=getattr(se lf.b,fn.__name_ _)
return fnew(*args,**kw )
setattr(A,fn.__ name__,delegate )
return fn
Aaron "Castironpi " Brady wrote:
>I kept this part of the problem out of this discussion as I'm pretty sure I can fill those in once I figure out the basic problem of auto-population of proxy methods since for each class/method those are going to be nearly identical. If I can autogenerate those on-the-fly I'm pretty sure I can add some extra-logic to them as well including signature change where A::bmethod(self ,c,x) would become A::bmethod(sel f,x) etc.
Do you want to couple instances or classes together?
It would be nice to have objects of B, C and D classes not knowing that they
are proxied (as they are used on their own too, not only inside of A
objects).
If A always proxies for B, C, and D, then the wrapper solution isn't
bad.
the whole purpose of A is pretty much to proxy and filter. It's got some
extra logic to combine and manipulate b, c and d objects inside of A class
objects.
If you're going to be doing any instance magic, that can change
the solution a little bit.
There's also a revision of the first implementation of Aproxy you
posted, which could stand alone as you have it, or work as a
classmethod or staticmethod.
def Aproxy(fn):
def delegate(self,* args,**kw):
print "%s::%s" % (args[0].__class__.__na me__,fn.__name_ _)
fnew=getattr(se lf.b,fn.__name_ _)
return fnew(*args,**kw )
setattr(A,fn.__ name__,delegate )
return fn
yep, that does look nicer/cleaner :) This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics |
by: Sandy Norton |
last post by:
If we are going to be stuck with @decorators for 2.4, then how about
using blocks and indentation to elminate repetition and increase
readability:
Example 1
---------
class Klass:
def __init__(self, name):
|
by: Steven Bethard |
last post by:
I think one of the biggest reasons we're having such problems coming
to any agreement on decorator syntax is that each proposal makes a
number of syntax decisions, not just one. For decorators, I see the
following decisions that must be made:
1) Indicator
Proposals differ on how some sort of indicator of "decoratorhood" is
use. These include:
* none (e.g. just the list, as in the "list-after-def" suggestion)
* the '@' character
|
by: Ville Vainio |
last post by:
It might just be that @decorator might not be all that bad. When you
look at code that uses it it's not that ugly after all.
A lot of the furor about this is probably because it happened so
quicly. The situation might have been different if we had seen a
pronouncement a week before, in the vein of "I have chosen this syntax
- it will go in to the next alpha".
My chief worry was throwing away one of the few unused ascii symbols,
but if...
|
by: Bengt Richter |
last post by:
ISTM that
@limited_expression_producing_function
@another
def func(): pass
is syntactic sugar for creating a hidden list of functions. (Using '|' in place of '@'
doesn't change the picture much (except for people whose tools depend on '@' ;-)).
I.e., (not having the source or time to delve) the apparent semantics of the above
is something roughly like
|
by: Ron_Adam |
last post by:
I was having some difficulty figuring out just what was going on with
decorators. So after a considerable amount of experimenting I was
able to take one apart in a way. It required me to take a closer look
at function def's and call's, which is something I tend to take for
granted.
I'm not sure this is 100%, or if there are other ways to view it, but
it seems to make sense when viewed this way.
Is there a way to do this same thing...
| |
by: Ron_Adam |
last post by:
Hi, Thanks again for all the helping me understand the details of
decorators.
I put together a class to create decorators that could make them a lot
easier to use.
It still has a few glitches in it that needs to be addressed.
(1) The test for the 'function' object needs to not test for a string
but an object type instead.
|
by: Michele Simionato |
last post by:
could ildg wrote:
> I think decorator is a function which return a function, is this
right?
> e.g. The decorator below if from
http://www.python.org/peps/pep-0318.html#id1.
>
> def accepts(*types):
> def check_accepts(f):
> assert len(types) == f.func_code.co_argcount
|
by: glen.coates.bigworld |
last post by:
I'm developing a library at the moment that involves many classes, some
of which have "exposed" capabilities. I'm trying to design a nice
interface for both exposing those capabilities, and inspecting
instances to find out what capabilities they have.
At the moment, I'm leaning towards a superclass (Exposed) that defines
a static method which is a decorator (expose) such that any derived
class can mark a method with @Exposed.expose and...
|
by: thomas.karolski |
last post by:
Hi,
I would like to create a Decorator metaclass, which automatically
turns a class which inherits from the "Decorator" type into a
decorator.
A decorator in this case, is simply a class which has all of its
decorator implementation inside a decorator() method. Every other
attribute access is being proxied to decorator().getParent().
Here's my attempt:
-------------------------------------------------------
|
by: marktang |
last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look !
Part I. Meaning of...
|
by: Hystou |
last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it.
First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
| |
by: Oralloy |
last post by:
Hello folks,
I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>".
The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed.
This is as boiled down as I can make it.
Here is my compilation command:
g++-12 -std=c++20 -Wnarrowing bit_field.cpp
Here is the code in...
|
by: jinu1996 |
last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth.
The Art of Business Website Design
Your website is...
|
by: tracyyun |
last post by:
Dear forum friends,
With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
|
by: agi2029 |
last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own....
Now, this would greatly impact the work of software developers. The idea...
|
by: isladogs |
last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM).
In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules.
He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms.
Adolph will...
|
by: 6302768590 |
last post by:
Hai team
i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
| |
by: bsmnconsultancy |
last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...
| |