pre-PEP generic objects

S

Scott David Daniels

Nick said:
Scott David Daniels said:
You can simplify this:
class Hash(object):
def __init__(self, **kwargs):
for key,value in kwargs.items():
setattr(self, key, value)
__getitem__ = getattr
__setitem__ = setattr
That doesn't work unfortunately...

Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: getattr expected at least 2 arguments, got 1

I'm not exactly sure why though!

I could have sworn I tested this, but I must have accidentally
tested h.a rather than h['a']. I don't know why it doesn't work either.
For example:

def gattr(*args): return getattr(*args)
def sattr(*args): return setattr(*args)
class Hash(object):
def __init__(self, **kwargs):
for key,value in kwargs.items():
setattr(self, key, value)
__getitem__ = gattr
__setitem__ = sattr

does work.

--Scott David Daniels
(e-mail address removed)
 
I

Istvan Albert

Steven said:
I promised I'd put together a PEP for a 'generic object' data type for
Python 2.5 that allows one to replace __getitem__ style access with
dotted-attribute style access (without declaring another class). Any
comments would be appreciated!

IMHO this too easy to accomplish right now to warrant
an "official" implementation:

class Bunch:
pass

b = Bunch()
b.one, b.two, b.three = 1,2,3

works just fine, depending on the problem I might add a few special
operators. For anything more complicated I'd rather write a real class.

Istvan.
 
S

Steven Bethard

Istvan said:
IMHO this too easy to accomplish right now to warrant
an "official" implementation:

class Bunch:
pass

b = Bunch()
b.one, b.two, b.three = 1,2,3

works just fine, depending on the problem I might add a few special
operators. For anything more complicated I'd rather write a real class.

You'll note that my implementation really isn't much more than this. (A
little bit extra to make converting hierarchies easier.) The question
is not how easy it is to write, but how many times it's going to get
written. If you're going to write your 2-line Bunch class (which should
probably be the 3-line Bunch class that uses new-style classes) a
thousand times, I think including a class that does this for you (and
provides a few other nice properties) is a Good Thing. If you're only
ever going to use it once, then yes, there's probably no reason to
include it in the stdlib.

The belief that I gathered from the end of the previous thread
discussing this (check last week's python-list I think) was that there
were a significant number of people who had wanted a class like this
(notably IPython), and more than one of them had rewritten the class a
few times.

Steve
 
P

Paul Rubin

Steven Bethard said:
...
The belief that I gathered from the end of the previous thread
discussing this (check last week's python-list I think) was that there
were a significant number of people who had wanted a class like this
(notably IPython), and more than one of them had rewritten the class a
few times.

I've written that class more than a few times myself, and ended up
adding operations to print the objects (show the member values),
serialize them (don't output any member whose name starts with _), etc.

I think it would be worthwhile to standardize something like this.
 
N

Nick Craig-Wood

Peter said:
Functions written in Python have a __get__ attribute while builtin
functions (implemented in C) don't. Python-coded functions therefore
automatically act as descriptors while builtins are just another
attribute.

Jp Calderone said:
When the class object is created, the namespace is scanned for
instances of <type 'function'>. For those and only those, a
descriptor is created which will produce bound and unbound methods.
Instances of other types, such as <type 'int'> or <type
'builtin_function_or_method'>, are ignored, leading to the critical
difference in this case:

I think I finally understand now - thank you to you both!
 
S

Steve Holden

Paul said:
I've written that class more than a few times myself, and ended up
adding operations to print the objects (show the member values),
serialize them (don't output any member whose name starts with _), etc.

I think it would be worthwhile to standardize something like this.

For this reason a PEP would have value: if it's rejected, the reasons
for its rejection will be recorded for posterity. If it isn't rejected,
of course, we get a bunch as part of the included batteries.

Next question: bunch is a cute name, but not very suggestive of purpose.
Who can think of a better one?

regards
Steve
 
C

Carlos Ribeiro

Next question: bunch is a cute name, but not very suggestive of purpose.
Who can think of a better one?

"Better"is highly subjective in this context. There are several alternatives:

Bunch
Generic
Record
DataRecord

I use "DataRecord" in my own code, because it's readable and makes it
clear that it's not a full fledged class, but a data-only structure.

Given my Pascal roots, I'm also fond of "Record". But given that many
people actually loathe Pascal (probably for reasons deeply rooted in a
bad academic experience :), that may be a big "turn off".

--
Carlos Ribeiro
Consultoria em Projetos
blog: http://rascunhosrotos.blogspot.com
blog: http://pythonnotes.blogspot.com
mail: (e-mail address removed)
mail: (e-mail address removed)
 
I

Istvan Albert

Steven said:
> The question is not how easy it is to write,
> but how many times it's going to get written.

but with that logic we could create a standard
"looping" construct called loop(x) that would stand in for

for i in range(x):

or a file_reader('whatever') generator that would be
a shortcut for:

for line in file('whatever'):
line = line.strip()
elems = line.split()
> and more than one of them had rewritten the class a few times.

Two observations regarding that:

1. Probably not entirely true. They might have used something like a Bunch
but it is a bit too optimistic to believe that they could have directly used
your Bunch. My Bunches turn out to be just a ever so slightly different.
Either have an update operation or an equality, or can be hashed etc.
So in the end it might save a lot less work.

2. Even if it was, no big deal. It takes too little time to do it.

On the other hand, it would be nice to have a module that
implements various design patterns. The Bunch, the Borg, the Null,
the Proxy all nicely documented tucked away in their separate
module. That would feel a lot less like littering the standard name space
with an class that just "seems" to be useful.

just an opinion.

Istvan
 
R

Remy Blank

Istvan said:
On the other hand, it would be nice to have a module that
implements various design patterns. The Bunch, the Borg, the Null,
the Proxy all nicely documented tucked away in their separate
module. That would feel a lot less like littering the standard name space
with an class that just "seems" to be useful.

+1 for a "patterns" module.

-- Remy


Remove underscore and suffix in reply address for a timely response.
 
C

Carlos Ribeiro

On the other hand, it would be nice to have a module that
implements various design patterns. The Bunch, the Borg, the Null,
the Proxy all nicely documented tucked away in their separate
module. That would feel a lot less like littering the standard name space
with an class that just "seems" to be useful.

Great idea. I have explored some ideas relating generic objects with
"standard" patterns, namely "observer" & "proxy".

One of the applications of generic objects is to allow for data
sharing between objects of different classes that happen to share a
few attributes; sometimes it's not possible to pass the entire object
around for the other one to copy the data, either because of safety
reasons, or because there is some slightly mismatch between the
interfaces. The generic, in this case, can be implemented either as an
intermediate storage structure (as a data-only records) or as an
"proxy" that exposes only the desired attributes. In this case, data
is passed around and copied manually, using the generic (or the
"proxy" object).

Another use case is when one wants to implement the "observer" pattern
(which would allow for a "live" data sharing). One alternative is to
use a "generic" as the base "observable' object; other objects can
register with the generic and simply receive notifications, or even
share the references to its internal attributes (btw, Python
descriptors & properties are incredibly useful in this case).
Extending it even further -- the observed object could accept
modifications from its observers, although in this case we're already
talking a about more complex pattern than the standard "observer"
(btw, how is it called?).

--
Carlos Ribeiro
Consultoria em Projetos
blog: http://rascunhosrotos.blogspot.com
blog: http://pythonnotes.blogspot.com
mail: (e-mail address removed)
mail: (e-mail address removed)
 
I

Ian Bicking

Istvan said:
but with that logic we could create a standard
"looping" construct called loop(x) that would stand in for

for i in range(x):

IMHO, that example is quite the opposite of what you are advocating.
range() is a simple function, and one which could be written by anyone
with little chance of flaw. Why not do:

i = 0
while i < x
...
i += 1

? The three arguments to range are all present (how valuable are a few
defaults). It's more general. You can extend it and modify it easily.
These are all the same properties of bunch. Easy things have value;
maybe that applies here, but saying that it is easy is not the same as
saying it isn't valuable. For instance, we now have the sorted
function, equivalent to:

def sorted(lst, *args, **kw):
lst = list(list)
lst.sort(*args, **kw)
return lst

People have been writing this function over and over. Only there are a
few subtlely different ways you can implement it, and you'll never know
which from the function name alone. dict's keyword argument constructor
is similar. And maybe bunch is similar; at least, I'd argue for or
against it based on that, not merely on how easy it is to reimplement.
"If the implementation is easy to explain, it may be a good idea."
(import this)

If it existed, I'd probably use it some where I'm currently using
dictionaries (but where I don't want to give the data behavior), and I'd
probably subclass it. Now, when it doesn't exist, I frequently
implement the behavior in classes that have other logic as well, and I
almost never use it without logic; I use dict() with its keyword
argument constructor, and just suffer the quotation marks when I'm
retrieving members.

BTW, in class that look like bunch, I usually implement it like:

class bunch(object):
def __init__(self, **kw):
for name, value in kw.items():
# IMPORTANT! This is subclass friendly: updating __dict__
# is not!
setattr(self, name, value)

def __call__(self, **kw):
# I'm not entirely happy with this:
new_values = self.__dict__.copy()
new_values.update(kw)
return self.__class__(**new_values)
 
M

Mel Wilson

I believe what Peter Otten was pointing out is that calling __eq__ is
not the same as using ==, presumably because the code for == checks the
types of the two objects and returns False if they're different before
the __eq__ code ever gets called.

Doesn't seem to:



Python 2.3 (#46, Jul 29 2003, 18:54:32) [MSC v.1200 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information..... def __eq__ (self, other):
.... return True
........ def __eq__ (self, other):
.... return False
....


Regards. Mel.
 
S

Steven Bethard

Istvan said:
On the other hand, it would be nice to have a module that
implements various design patterns. The Bunch, the Borg, the Null,
the Proxy all nicely documented tucked away in their separate
module. That would feel a lot less like littering the standard name space
with an class that just "seems" to be useful.

From the (pre-)PEP:

"This PEP proposes a standard library addition..."

Bunch would go into the standard library (probably the collections
module) not to the __builtins__. I don't see how this "litters the
standard namespace".

Steve
 
S

Steven Bethard

Ian said:
class bunch(object):
def __init__(self, **kw):
for name, value in kw.items():
# IMPORTANT! This is subclass friendly: updating __dict__
# is not!
setattr(self, name, value)

Good point about being subclass friendly... I wonder if there's an easy
way of doing what update does though... Update (and therefore __init__)
allows you to pass in a Bunch, dict, (key, value) sequence or keyword
arguments by taking advantage of dict's update method. Is there a clean
way of supporting all these variants using setattr?

Steve
 
S

Steven Bethard

Mel said:
Doesn't seem to:
[snip]

Hmm... maybe it only shows up with subclassing?
.... def __eq__(self, other):
.... return True
........ def __eq__(self, other):
.... return False
....False


STeve
 
N

Nick Coghlan

Steven said:

I suspect that example is due to the rule that "A op B" can be passed to any of
the following, depending on the operator and the types of A and B:

A.__op__(B)
B.__op__(A)
B.__rop__(A)

The latter two get invoked when B is a proper subclass of A (using 'op' for
commutative operations, and 'rop' for potentially non-commutative ones). This is
so that subclasses can interact with parent classes correctly.

So, in Steven's original code, B.__eq__(A) was invoked, and returned False
(since A was the parent class of B, not a subclass).

Cheers,
Nick.
 
I

Istvan Albert

Steven said:
module) not to the __builtins__. I don't see how this "litters the
standard namespace".

Maybe then it doesn't.

but what are you saying? that a man cannot exaggerate and
fudge the facts in order to embellish his argument? :)

Istvan.
 
S

Steven Bethard

Istvan said:
but what are you saying? that a man cannot exaggerate and
fudge the facts in order to embellish his argument? :)

Heh heh. Yeah, something like that. ;)

Steve
 
M

Mel Wilson

Mel said:
Doesn't seem to:
[snip]

Hmm... maybe it only shows up with subclassing?

:) Seems to:


Python 2.3 (#46, Jul 29 2003, 18:54:32) [MSC v.1200 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information..... def __eq__(self, other):
.... return True
........ def __eq__(self, other):
.... print "(according to Neq)"
.... return False
....(according to Neq)
False(according to Neq)
False


Regards. Mel.
 

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

No members online now.

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,111
Latest member
KetoBurn
Top