Storing objects required by functions.

D

David M. Wilson

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.
 
J

John Roth

David M. Wilson said:
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*
:)

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
 
A

Aahz

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?
 
M

Matt Goodall

David said:
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
 
J

John Roth

Aahz said:
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
http://www.pythoncraft.com/
 
T

Terry Reedy

David M. Wilson said:
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
 
J

Jp Calderone

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.


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

_hex = re.compile()

Why should it be local-use-only?

Jp

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (GNU/Linux)

iD8DBQE/8dMYedcO2BJA+4YRAgWbAJwNC9zferduRJarhPQbRBltnJWw9gCglSJA
c/gKhA2op4Y7Sg43ugSdG+A=
=ku9c
-----END PGP SIGNATURE-----
 
J

John Roth

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

news:[email protected]...
 
J

Jp Calderone

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

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
 
B

Bengt Richter

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.
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.

Regards,
Bengt Richter
 
J

John Roth

Jp Calderone said:
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 ;)

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
 
C

Christos TZOTZIOY Georgiou

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...
 
B

Bengt Richter

It's much more general to use new.instancemethod. See:
I think I did use new.instancemethod, through another door ;-)
... pass
... True
;-)
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??
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...
Interesting. Gotta go.

Regards,
Bengt Richter
 
C

Christos TZOTZIOY Georgiou

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...
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 ;)
 
A

Aahz

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,174
Messages
2,570,940
Members
47,486
Latest member
websterztechnologies01

Latest Threads

Top