Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Storing objects required by functions.

24 views
Skip to first unread message

David M. Wilson

unread,
Dec 30, 2003, 9:55:27 AM12/30/03
to
Further to my last post here, I was playing some more with building a
regex object and storing it somewhere for use internally by a
function. I'm not happy with any of my solutions:


# I don't like this, but the fact that you can modify the procedure's
# function via a named argument seems neat in a hacky sort of way.

def uses_default_parm_yuck(x, r = re.compile("...")):
pass


g = re.compile('...')

def uses_global_yuck(x):
global g
pass


# This is horrible and probably slow.

class is_hex:
def __init__(self):
self.r = re.compile('...')

def __call__(self, x):
r = self.r
pass

is_hex = is_hex()


# This mucks up scoping so that your procedure can't access it's
# parent scope like it could normally. Since I never do this,
# it's my favourite.

def is_hex():
r = re.compile('...')
def is_hex(s):
return r.match(s) is not None
return is_hex

is_hex = is_hex()

Am I missing something? Is there a nicer way of doing this? On a day
to day basis I find myself in this situation quite regularly, and now
I come to think of it today, it is something that I would like to
improve.

It is funny that in private it has never bothered me much, but when
posting on comp.lang.python I find that the code is unacceptable.
Maybe I have two modes of programming, idealist and practical? *shrug*
:)


David.

John Roth

unread,
Dec 30, 2003, 10:11:54 AM12/30/03
to

"David M. Wilson" <dw-goo...@botanicus.net> wrote in message
news:99dce321.03123...@posting.google.com...

Your second solution (the one you labeled "horrible and probably
slow") is a classic example of a Function Object, and it's described
that way in "Design Patterns". AFAIK, it's got the same call overhead
as any other way of doing the job. I'd call it the cleanest way of doing
a parameterized function.

John Roth
>
>
> David.


Aahz

unread,
Dec 30, 2003, 10:33:59 AM12/30/03
to
In article <99dce321.03123...@posting.google.com>,

David M. Wilson <dw-goo...@botanicus.net> wrote:
>
>g = re.compile('...')
>
>def uses_global_yuck(x):
> global g
> pass

Why not just use the global? Without the ``global`` statement, that is.
Don't like that? How's it any different from using a class instance?
--
Aahz (aa...@pythoncraft.com) <*> http://www.pythoncraft.com/

Weinberg's Second Law: If builders built buildings the way programmers wrote
programs, then the first woodpecker that came along would destroy civilization.

John Roth

unread,
Dec 30, 2003, 10:58:13 AM12/30/03
to

"Aahz" <aa...@pythoncraft.com> wrote in message
news:bss5t7$1tf$1...@panix1.panix.com...

> In article <99dce321.03123...@posting.google.com>,
> David M. Wilson <dw-goo...@botanicus.net> wrote:
> >
> >g = re.compile('...')
> >
> >def uses_global_yuck(x):
> > global g
> > pass
>
> Why not just use the global? Without the ``global`` statement, that is.
> Don't like that? How's it any different from using a class instance?

You can't parameterize it. You've got one global, while
you can put different parameters in different class instances.
He isn't doing that here, but that's the major place where the
Function Object design pattern shines.

John Roth

Matt Goodall

unread,
Dec 30, 2003, 10:38:15 AM12/30/03
to David M. Wilson, pytho...@python.org
David M. Wilson wrote:

>Further to my last post here, I was playing some more with building a
>regex object and storing it somewhere for use internally by a
>function. I'm not happy with any of my solutions:
>
>
># I don't like this, but the fact that you can modify the procedure's
># function via a named argument seems neat in a hacky sort of way.
>
>def uses_default_parm_yuck(x, r = re.compile("...")):
> pass
>
>
>g = re.compile('...')
>
>def uses_global_yuck(x):
> global g
> pass
>
>

There is no need to define g as global unless you actually need to
rebind g inside the function.

>
># This is horrible and probably slow.
>
>class is_hex:
> def __init__(self):
> self.r = re.compile('...')
>
> def __call__(self, x):
> r = self.r
> pass
>
>is_hex = is_hex()
>
>

I doubt it's that much slower (if at all). You should profile it to check.

>
># This mucks up scoping so that your procedure can't access it's
># parent scope like it could normally. Since I never do this,
># it's my favourite.
>
>def is_hex():
> r = re.compile('...')
> def is_hex(s):
> return r.match(s) is not None
> return is_hex
>
>is_hex = is_hex()
>
>
>
>Am I missing something? Is there a nicer way of doing this? On a day
>to day basis I find myself in this situation quite regularly, and now
>I come to think of it today, it is something that I would like to
>improve.
>
>It is funny that in private it has never bothered me much, but when
>posting on comp.lang.python I find that the code is unacceptable.
>Maybe I have two modes of programming, idealist and practical? *shrug*
>:)
>
>
>David.
>
>

Another alternative relies on the fact that functions themselves are
objects so you could do this:

def is_hex(s):
return is_hex.r.match(s) is not None

is_hex.r = re.compile(...)


Although, for simplicity, I would probably just make the compiled regex
a module scope variable with a sensible name, maybe even using the _
prefix to hint that noone should touch it:

_is_hex_regex = re.compile(...)

def is_hex(s):
return _is_hex_regex.match(s) is not None


Cheers, Matt

--
Matt Goodall, Pollenation Internet Ltd
w: http://www.pollenationinternet.com
e: ma...@pollenation.net

Terry Reedy

unread,
Dec 30, 2003, 12:43:13 PM12/30/03
to

"David M. Wilson" <dw-goo...@botanicus.net> wrote in message
news:99dce321.03123...@posting.google.com...

> Further to my last post here, I was playing some more with building a
> regex object and storing it somewhere for use internally by a
> function. I'm not happy with any of my solutions:

> # I don't like this, but the fact that you can modify the procedure's
> # function via a named argument seems neat in a hacky sort of way.
>
> def uses_default_parm_yuck(x, r = re.compile("...")):
> pass

I default args less bothersome than some people. The only problem for me
is that a caller may accidentally give nonesense second param. If this can
silently give a junk answer, this is not very acceptable. In this case,
random objects without a match() method would raise an exception.

> g = re.compile('...')

Use _ to indicate 'private' or 'local-use-only' status.

_hex = re.compile()

> def uses_global_yuck(x):
> global g
> pass

The global declaration is not needed for read-only access and is therefore
misleading. So delete and just use _hex in the body. Python functions
constantly use globals and builtins, both functions and other values, in
read-only mode. So I see no problem with this standard Python idiom.

> # This is horrible and probably slow.
>
> class is_hex:
> def __init__(self):
> self.r = re.compile('...')
>
> def __call__(self, x):
> r = self.r
> pass

One should at least generalize this to re_matcher and pass specific re to
init. OO purists might prefer this but I agree that it is overkill unless,
possibly, one has lots of instances. OO was made for man, not man for OO
purity.

> is_hex = is_hex()

is_hex = re_matcher(hex_re)

> # This mucks up scoping so that your procedure can't access it's
> # parent scope like it could normally.

Funny, you just objected above to having a function access is parent scope
for the re.

> def is_hex():
> r = re.compile('...')
> def is_hex(s):
> return r.match(s) is not None
> return is_hex

> is_hex = is_hex()

Same comment: generalize

def re_matcher(some_re):
r = re.compile(some_re)
def is_some(s):


return r.match(s) is not None

return is_some

is_hex = re_matcher(hex_re)

I would use this in preverence to class version for making multiple
matchers. I think closures are great as one-method instances (or as
callable no-method instances, if one prefers).

Terry J. Reedy


Jp Calderone

unread,
Dec 30, 2003, 2:33:44 PM12/30/03
to pytho...@python.org
On Tue, Dec 30, 2003 at 12:43:13PM -0500, Terry Reedy wrote:
>
> I default args less bothersome than some people. The only problem for me
> is that a caller may accidentally give nonesense second param. If this can
> silently give a junk answer, this is not very acceptable. In this case,
> random objects without a match() method would raise an exception.
>
> > g = re.compile('...')
>
> Use _ to indicate 'private' or 'local-use-only' status.
>
> _hex = re.compile()
>

Why should it be local-use-only?

Jp

signature.asc

John Roth

unread,
Dec 30, 2003, 3:22:09 PM12/30/03
to
Could you please *not* add whatever signature or business
card or whatever you're adding to your messages? My anti-virus
blocks access to your entire message when you do that,
and it's a royal pain to save the message and then disassemble
it to find you made one line of comment.

John Roth

"Jp Calderone" <exa...@intarweb.us> wrote in message
news:mailman.51.10728129...@python.org...


Jp Calderone

unread,
Dec 30, 2003, 5:43:01 PM12/30/03
to pytho...@python.org

It's a PGP signature. If the mail client you're using can't extricate the
plaintext part, I'd say it isn't a very good mail client. (Even mailman's
archiver deals with them the right way these days ;)

Jp

Bengt Richter

unread,
Dec 30, 2003, 7:08:47 PM12/30/03
to
On 30 Dec 2003 06:55:27 -0800, dw-goo...@botanicus.net (David M. Wilson) wrote:

>Further to my last post here, I was playing some more with building a
>regex object and storing it somewhere for use internally by a
>function. I'm not happy with any of my solutions:
>
>
># I don't like this, but the fact that you can modify the procedure's
># function via a named argument seems neat in a hacky sort of way.
>
>def uses_default_parm_yuck(x, r = re.compile("...")):
> pass
>

There is another way, but IMO it would be nice to be able to have an optional
second parenthesized list to make bindings the same way which wouldn't be part
of the call signature. I.e.,

def uses_default_parm_yuck(x)(r = re.compile("...")):
pass

or, same thing more prominently:

def uses_default_parm_yuck(x)(
# pre-bound locals


r = re.compile("...")

):
pass

ISTM most of the implementation pieces for that should already be there.

The other way is to take advantage of functions' roles as decriptors and the mechanism that
makes bound methods with a self as the first arg, but the rest apparently normal. I.e,
we can put the r parameter in the place of self (not specifically tested)

def uses_self(r, x):
pass
uses_self = uses_self.__get__(re.compile("..."))

then (the rebound) uses_self will look like a bound method and want exactly one parameter x
(or whatever signature you program) and be able to refer to r like self.

Regards,
Bengt Richter

John Roth

unread,
Dec 30, 2003, 7:19:19 PM12/30/03
to

"Jp Calderone" <exa...@intarweb.us> wrote in message
news:mailman.58.10728243...@python.org...

Actually, whatever is doing it simply sequesters almost anything,
including perfectly innocuous graphics. The difficulty is that the
mail client still displays most of the sequestered attachments, but
for some reason if it can't figure out one attachment, it doesn't
display any of them.

That means I've got to save the entire mail and use a command line
tool to break it into pieces. Python's mail tools do a reasonable
(not excellent) job of this, so nothing is lost, but it's more work
than I usually want to go through for newsgroup messages.

John Roth


>
> Jp
>


Christos TZOTZIOY

unread,
Dec 30, 2003, 9:04:47 PM12/30/03
to
On 31 Dec 2003 00:08:47 GMT, rumours say that bo...@oz.net (Bengt
Richter) might have written:

>The other way is to take advantage of functions' roles as decriptors and the mechanism that
>makes bound methods with a self as the first arg, but the rest apparently normal. I.e,
>we can put the r parameter in the place of self (not specifically tested)
>
> def uses_self(r, x):
> pass
> uses_self = uses_self.__get__(re.compile("..."))

It's much more general to use new.instancemethod. See:

def voodoo(function, *its_arguments):
from new import instancemethod
def child(function, first_argument, *rest_of_arguments):
if rest_of_arguments:
return child(
instancemethod(function, first_argument, object),
*rest_of_arguments
)
else:
return instancemethod(function, first_argument, object)
return child(function, *its_arguments)

The import statement is in the voodoo just for completeness including it
here.
The function above recurses in order to allow stuff like:

getter = voodoo(getattr, my_object, "its_attribute")

or the more modern

getter = voodoo(operator.itemgetter("its_attribute"), my_object)

and similarly

setter = voodoo(operator, my_object, "its_attribute")

allowing

setter(value)

at good speeds.


I have a module predicates.py defining All and Any classes for
iterables, and the trick above plus itertools allows *some* operations
to run faster than correspondent python code...
--
TZOTZIOY, I speak England very best,
Ils sont fous ces Redmontains! --Harddix

Bengt Richter

unread,
Dec 30, 2003, 11:07:01 PM12/30/03
to
On Wed, 31 Dec 2003 04:04:47 +0200, Christos "TZOTZIOY" Georgiou <tz...@sil-tec.gr> wrote:

>On 31 Dec 2003 00:08:47 GMT, rumours say that bo...@oz.net (Bengt
>Richter) might have written:
>
>>The other way is to take advantage of functions' roles as decriptors and the mechanism that
>>makes bound methods with a self as the first arg, but the rest apparently normal. I.e,
>>we can put the r parameter in the place of self (not specifically tested)
>>
>> def uses_self(r, x):
>> pass
>> uses_self = uses_self.__get__(re.compile("..."))
>
>It's much more general to use new.instancemethod. See:

I think I did use new.instancemethod, through another door ;-)

>>> def uses_self(r, x):
... pass
...
>>> import re


>>> uses_self = uses_self.__get__(re.compile("..."))

>>> type(uses_self)
<type 'instancemethod'>

>>> myim = type(uses_self)
>>> myim
<type 'instancemethod'>

>>> import new
>>> myim is new.instancemethod
True
;-)

>>> def foo(self, *args, **kw): print 'self=%r, args=%r, kw=%r'%(self, args, kw)
...
>>> myim(foo, 'dummy self', object) # object per your usage
<bound method object.foo of 'dummy self'>
>>> foom = myim(foo, 'dummy self', object) # object per your usage
>>> foom(1,2,3,hi='Hello')
self='dummy self', args=(1, 2, 3), kw={'hi': 'Hello'}

I didn't make voodoo out of it though. Interesting, but all that nested
calling at call-time seems like it would make for a performance hit? Unless maybe it is
all packed up in slots that C can get to very fast??

Interesting. Gotta go.

Regards,
Bengt Richter

Christos TZOTZIOY

unread,
Dec 30, 2003, 11:20:56 PM12/30/03
to
On 31 Dec 2003 04:07:01 GMT, rumours say that bo...@oz.net (Bengt
Richter) might have written:

>>It's much more general to use new.instancemethod. See:

>I think I did use new.instancemethod, through another door ;-)

No doubt :) . *I* wasn't clear: new.instancemethod accepts any callable
I throw to it, while not all callables have a __get__ method...

>>>> foom(1,2,3,hi='Hello')
> self='dummy self', args=(1, 2, 3), kw={'hi': 'Hello'}
>
>I didn't make voodoo out of it though. Interesting, but all that nested
>calling at call-time seems like it would make for a performance hit? Unless maybe it is
>all packed up in slots that C can get to very fast??

Actually, I think that most overhead lies in creating a frame object for
the execution of a python callable. Calling C functions is fastest. I
didn't spot any delays, but I didn't search thoroughly ;)

Aahz

unread,
Dec 30, 2003, 11:29:58 PM12/30/03
to
In article <mailman.58.10728243...@python.org>,

Jp Calderone <exa...@intarweb.us> wrote:
>
>It's a PGP signature. If the mail client you're using can't extricate the
>plaintext part, I'd say it isn't a very good mail client. (Even mailman's
>archiver deals with them the right way these days ;)

I'm using netnews, not e-mail. PGP doesn't belong in netnews posts, IMO.

Weinberg's Second Law: If builders built buildings the way programmers wrote

0 new messages