By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,921 Members | 1,433 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 434,921 IT Pros & Developers. It's quick & easy.

pre-PEP generic objects

P: n/a
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!

Thanks!

Steve

----------------------------------------------------------------------
Title: Generic Object Data Type
Version: $Revision: 1.0 $
Last-Modified: $Date: 2004/11/29 16:00:00 $
Author: Steven Bethard <st************@gmail.com>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 29-Nov-2004
Python-Version: 2.5
Post-History: 29-Nov-2004
Abstract
========

This PEP proposes a standard library addition to support the simple
creation of 'generic' objects which can be given named attributes
without the need to declare a class. Such attribute-value mappings are
intended to complement the name-value mappings provided by Python's
builtin dict objects.
Motivation
==========

Python's dict objects provide a simple way of creating anonymous
name-value mappings. These mappings use the __getitem__ protocol to
access the value associated with a name, so that code generally appears
like::

mapping['name']

Occasionally, a programmer may decide that dotted-attribute style access
is more appropriate to the domain than __getitem__ style access, and
that their mapping should be accessed like::

mapping.name

Currently, if a Python programmer makes this design decision, they are
forced to declare a new class, and then build instances of this class.
When no methods are to be associated with the attribute-value mappings,
declaring a new class can be overkill. This PEP proposes adding a
simple type to the standard library that can be used to build such
attribute-value mappings.

Providing such a type allows the Python programmer to determine which
type of mapping is most appropriate to their domain and apply this
choice with minimal effort. Some of the suggested uses include:
Returning Named Results
-----------------------

It is often appropriate for a function that returns multiple items to
give names to the different items returned. The type suggested in this
PEP provides a simple means of doing this that allows the returned
values to be accessed in the usual attribute-style access::
def f(x): ... return Bunch(double=2*x, squared=x**2)
... y = f(10)
y.double 20 y.squared 100
Representing Hierarchical Data
------------------------------

The type suggested in this PEP also allows a simple means of
representing hierarchical data that allows attribute-style access::
x = Bunch(spam=Bunch(rabbit=1, badger=[2, 3, 4]), ham='neewom')
x.spam.badger [2, 3, 4] x.ham 'neewom'
Rationale
=========

As Bunch objects are intended primarily to replace simple classes,
simple Bunch construction was a primary concern. As such, the Bunch
constructor supports creation from keyword arguments, dicts, and
sequences of (attribute, value) pairs::
Bunch(eggs=1, spam=2, ham=3) Bunch(eggs=1, ham=3, spam=2) Bunch({'eggs':1, 'spam':2, 'ham':3}) Bunch(eggs=1, ham=3, spam=2) Bunch([('eggs',1), ('spam',2), ('ham',3)]) Bunch(eggs=1, ham=3, spam=2)

To allow attribute-value mappings to be easily combined, the update
method of Bunch objects supports similar arguments.

If Bunch objects are used to represent hierarchical data, comparison of
such objects becomes a concern. For this reason, Bunch objects support
object equality::
x = Bunch(parrot=Bunch(lumberjack=True, spam=42), peng='shrub')
y = Bunch(peng='shrub', parrot=Bunch(spam=42, lumberjack=True))
z = Bunch(parrot=Bunch(lumberjack=True), peng='shrub')
x == y True x == z False

Additionally, to allow users of the Bunch type to convert other
hierarchical data into Bunch objects, a frommapping classmethod is
supported. This can be used, for example, to convert an XML DOM tree
into a tree of nested Bunch objects::
import xml.dom.minidom
def getitems(element): ... if not isinstance(element, xml.dom.minidom.Element):
... raise TypeError('items only retrievable from Elements')
... if element.attributes:
... for key, value in element.attributes.items():
... yield key, value
... children = {}
... for child in element.childNodes:
... if child.nodeType == xml.dom.minidom.Node.TEXT_NODE:
... text_list = children.setdefault('text', [])
... text_list.append(child.nodeValue)
... else:
... children.setdefault(child.nodeName, []).append(
... Bunch.frommapping(child, getitems=getitems))
... for name, child_list in children.items():
... yield name, child_list
... doc = xml.dom.minidom.parseString("""\ ... <xml>
... <a attr_a="1">
... a text 1
... <b attr_b="2" />
... <b attr_b="3"> b text </b>
... a text 2
... </a>
... <c attr_c="4"> c text </c>
... </xml>""") b = Bunch.frommapping(doc.documentElement, getitems=getitems)
b.a[0].b[1] Bunch(attr_b=u'3', text=[u' b text '])

Note that support for the various mapping methods, e.g.
__(get|set|del)item__, __len__, __iter__, __contains__, items, keys,
values, etc. was intentionally omitted as these methods did not seem to
be necessary for the core uses of an attribute-value mapping. If such
methods are truly necessary for a given use case, this may suggest that
a dict object is a more appropriate type for that use.
Reference Implementation
========================

(This will be replaced with a link to a SF patch when I think I've
made all the necessary corrections)::

import operator as _operator

class Bunch(object):
"""Bunch([bunch|dict|seq], **kwds) -> new bunch with specified
attributes

The new Bunch object's attributes are initialized from (if
provided) either another Bunch object's attributes, a
dictionary, or a sequence of (name, value) pairs, then from the
name=value pairs in the keyword argument list.

Example Usage: Bunch(eggs=1, spam=2, ham=3) Bunch(eggs=1, ham=3, spam=2) Bunch({'eggs':1, 'spam':2, 'ham':3}) Bunch(eggs=1, ham=3, spam=2) Bunch([('eggs',1), ('spam',2), ('ham',3)]) Bunch(eggs=1, ham=3, spam=2) Bunch(Bunch(eggs=1, spam=2), ham=3) Bunch(eggs=1, ham=3, spam=2)
"""

def __init__(self, *args, **kwds):
"""Initializes a Bunch instance."""
self.update(*args, **kwds)

def __eq__(self, other):
"""x.__eq__(y) <==> x == y"""
return (isinstance(other, self.__class__)
and self.__dict__ == other.__dict__)

def __repr__(self):
"""x.__repr__() <==> repr(x)

If all attribute values in this bunch (and any nested
bunches) are reproducable with eval(repr(x)), then the Bunch
object is also reproducable for eval(repr(x)).
"""
return '%s(%s)' % (self.__class__.__name__,
', '.join('%s=%r' % (k, v)
for k, v
in self.__dict__.items()))

def update(self, *args, **kwds):
"""update([bunch|dict|seq], **kwds) -> None

Updates a Bunch object's attributes from (if provided)
either another Bunch object's attributes, a dictionary, or a
sequence of (name, value) pairs, then from the name=value
pairs in the keyword argument list.
"""
if len(args) == 1:
other, = args
if isinstance(other, self.__class__):
other = other.__dict__
try:
self.__dict__.update(other)
except TypeError:
raise TypeError('cannot update Bunch with %s' %
type(other).__name__)
elif len(args) != 0:
raise TypeError('expected 1 argument, got %i' %
len(args))
self.__dict__.update(kwds)

@classmethod
def frommapping(cls, mapping, getitems=None):
"""Create a Bunch object from a (possibly nested) mapping.

Note that, unlike the Bunch constructor, frommapping
recursively converts all mappings to bunches.

Example Usage: Bunch.frommapping({'eggs':1,

... 'spam':{'ham':2, 'badger':3}})
Bunch(eggs=1, spam=Bunch(ham=2, badger=3))

Keyword Arguments:
mapping -- a mapping object
getitems -- a function that takes the mapping as a parameter
and returns an iterable of (key, value) pairs. If not
provided, the items method on the mapping object will be
used, or (key, mapping[key]) values will be generated if
the mapping object does not provide an items method.

Note that getitems will be applied recursively to each value
in the mapping. It should raise a TypeError if it is
applied to an object for which it cannot produce
(key, value) pairs.
"""
# determine which items() method to use
if getitems is None:
try:
getitems = type(mapping).items
except AttributeError:
getitems = _items
# build the Bunch from the mapping, recursively
result = cls()
for key, value in getitems(mapping):
try:
value = cls.frommapping(value, getitems=getitems)
except TypeError:
pass
setattr(result, key, value)
return result
def _items(mapping):
"""Produces (key, value) pairs from a mapping object.

Intended for use with mapping objects that do not supply an
items method.
"""
for key in mapping:
yield key, mapping[key]
Open Issues
===========
What should the type be named? Some suggestions include 'Bunch',
'Record' and 'Struct'.

Where should the type be placed? The current suggestion is the
collections module.
References
==========

...
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
End:
Jul 18 '05 #1
Share this Question
Share on Google+
49 Replies


P: n/a
Steven Bethard wrote:
Currently, if a Python programmer makes this design decision, they are
forced to declare a new class, and then build instances of this class.


FORCED to create a new class, and FORCED to create instances of
their own class instead of your class? without this, Python must surely
be unusable. no wonder nobody's ever managed to use it for anything.

</F>

Jul 18 '05 #2

P: n/a
Steven Bethard wrote:
def*__eq__(self,*other):
"""x.__eq__(y)*<==>*x*==*y"""
return*(isinstance(other,*self.__class__)
and*self.__dict__*==*other.__dict__)


This results in an asymmetry:
from bunch import Bunch
class B(Bunch): pass .... B().__eq__(Bunch()) False Bunch().__eq__(B()) True

With indirect use of __eq__() this puzzling behaviour disappears:
B() == Bunch() False Bunch() == B()

False

Whether this is intended, I don't know. If someone can enlighten me...

In any case I would prefer self.__class__ == other.__class__ over
isinstance().

Peter
Jul 18 '05 #3

P: n/a
Steven Bethard <st************@gmail.com> wrote:
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!


This sounds very much like this class which I've used to convert perl
programs to python

class Hash:
def __init__(self, **kwargs):
for key,value in kwargs.items():
setattr(self, key, value)
def __getitem__(self, x):
return getattr(self, x)
def __setitem__(self, x, y):
setattr(self, x, y)

My experience from using this is that whenever I used Hash(), I found
that later on in the refinement of the conversion it became its own
class.

So my take on the matter is that this encourages perl style
programming (just ram it in a hash, and write lots of functions acting
on it) rather than creating a specific class for the job which is dead
easy in python anyway and to which you can attach methods etc.

YMMV ;-)

--
Nick Craig-Wood <ni**@craig-wood.com> -- http://www.craig-wood.com/nick
Jul 18 '05 #4

P: n/a
The proposed use cases sound more appropriate for a "named tuple" than any sort
of dictionary. (This may have been mentioned in previous discussions. I wasn't
keeping track of those, though)

Notice that I've used 'fromPairs' rather than 'fromMapping', since consistent
order matters for a tuple. Comparison semantics are inherited directly from
tuple, and don't care about names (they're only interested in values).

Also, it seems like there has to be a better way to do the "opposite of zip()"
in fromPairs(), but I sure as hell can't think of it.

Cheers,
Nick.
a = named_tuple(['x', 'y'], (3, 8))
a named_tuple(['x', 'y'], (3, 8)) a.x 3 a.y 8 str(a) '(3, 8)' b = named_tuple.fromPairs(sorted({'x':3, 'y':8}.items()))
b named_tuple(['x', 'y'], (3, 8)) b.x 3 b.y 8 str(b) '(3, 8)' a == b True


And the code for the above:

class named_tuple(tuple):
def __new__(cls, names, *args):
self = tuple.__new__(cls, *args)
self._names = dict(zip(names, range(len(names))))
return self

@staticmethod
def fromPairs(items):
names = [x[0] for x in items]
values = [x[1] for x in items]
return named_tuple(names, values)

def __getattr__(self, attr):
if attr in self._names:
return self[self._names[attr]]
else:
return tuple.__getattr__(attr)

def __repr__(self):
return "named_tuple(%s, %s)" % (str(self.names()),
str(tuple.__repr__(self)))

def __str__(self):
return tuple.__repr__(self)

def names(self):
return sorted(self._names.keys(), key=self._names.__getitem__)
Jul 18 '05 #5

P: n/a
On Tue, 30 Nov 2004 22:30:21 +1000, Nick Coghlan <nc******@email.com> wrote:
The proposed use cases sound more appropriate for a "named tuple" than any sort
of dictionary. (This may have been mentioned in previous discussions. I wasn't
keeping track of those, though)


I agree with it. I was involved in that discussion, and got the the
point of listing a few desired features. As I am currently involved
into other project, I left it as it was, but I'll resume working as
soon as I can. I really think that both (generic objects and named
tuples) are slighly different but still very similar approaches to the
same problem, so some sort of "unification" of the efforts may be
interesting.

But there's something more important: while reading this document, and
some of the replies, it became clear that the main point is to
understand whether this proposed feature (in any possible
implementation) is in fact useful enough to deserve a place in the
standard library, and also if it represents a good coding style. With
some risk of being way too simplistic, it's something like this:

-- The people that is favorable to this implementation argue that one
should not be required to create a new class just to return a bunch of
results.

-- The people that is against it point out that, as soon as you start
returning multiple values, it's probable that you'll need to implement
a class anyway, so it's better off to do it sooner and forget generics
(or named tuples) entirely.

I see some parallels between this discussion and another one about
polymorphism. It's considered good Python practice to rely on
interfaces, or protocols, when designing the call signature of a
function or method. So if you receive an arbitrary object, you should
not check if it's a descendand of some abstract parent type; that's
just too rigid, and forces people to deal with complex multiple
inheritance stuff, and that's really not needed in Python. There is a
better way: just check if it exposes the desired interface, or
protocol. The adaptation framework (as described by PEP 246, and
extended by the PyProtocols package) is a nice implementation of this
concept.

A "generic" return object is just like this, but for a different
scenario: an adaptable return value, that doesn't enforce a class
signature when assigning the return value of a function or method.
It's perfectly symmetrical to the usage of interfaces on call. I think
that's a better, and much more powerful argument for the
implementation of a generic class, and also, for some supporting
machinery for it.

Extending this reasoning, generic return objects (implemented either
as dictionary based, or as named tuples) could be seen as "adaptable"
return values. Upon return, one could get such a temporary structure
and assign its members into another, more complex class, that would
accept fields of the same name, but possibly include other fields and
extra functionality. For example: a function that returns a complex
time structure does not need to return a "time class". It may return a
generic, or named tuple, which is in turn can be assigned to an object
that exposes a 'compatible' assignment interface. This assignment can
be done by a method of the generic clas itself, according either to
the names of the member of the generics, or the order of the tuple,
depending on the scenario.

For now, that's all that I have to contribute into this discussion.
There's also a lot of stuff in the c.l.py archives regarding named
tuples and also generics that is surely worth checking.

--
Carlos Ribeiro
Consultoria em Projetos
blog: http://rascunhosrotos.blogspot.com
blog: http://pythonnotes.blogspot.com
mail: ca********@gmail.com
mail: ca********@yahoo.com
Jul 18 '05 #6

P: n/a
Fredrik Lundh wrote:
Steven Bethard wrote:
Currently, if a Python programmer makes this design decision, they are
forced to declare a new class, and then build instances of this class.
FORCED to create a new class, and FORCED to create instances of
their own class instead of your class?


I don't see any way to produce the same behavior without *someone* (the
user or the stdlib) declaring a class. If you see a way to do this
without a class declared somewhere, please let me know how...
without this, Python must surely be unusable.
I definitely agree. Python without classes would be quite unpleasant to
use.
no wonder nobody's ever managed to use it for anything.


Yeah, I don't think anyone's managed to use Python without classes for
years, especially since things like int, str, etc. were converted to types.

Steve
Jul 18 '05 #7

P: n/a
Peter Otten wrote:
Steven Bethard wrote:
def __eq__(self, other):
"""x.__eq__(y) <==> x == y"""
return (isinstance(other, self.__class__)
and self.__dict__ == other.__dict__)
This results in an asymmetry:

[snip]
Whether this is intended, I don't know. If someone can enlighten me...

In any case I would prefer self.__class__ == other.__class__ over
isinstance().


Unintended. I'll switch to
self.__class__ == other.__class__
or
type(self) == type(other)
Any preference?

Steve
Jul 18 '05 #8

P: n/a
Nick Craig-Wood wrote:
Steven Bethard <st************@gmail.com> wrote:
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!
My experience from using this is that whenever I used Hash(), I found
that later on in the refinement of the conversion it became its own
class.


This has also generally been my experience, though I'm not sure it's as
true for the XML DOM to Bunch translation. Did you use Hash() in the
same way for hierarchical data?
So my take on the matter is that this encourages perl style
programming (just ram it in a hash, and write lots of functions acting
on it) rather than creating a specific class for the job which is dead
easy in python anyway and to which you can attach methods etc.


You'll note that the (pre-)PEP explicitly notes that this object is
intended only for use when no methods are associated with the attributes:

"When no methods are to be associated with the attribute-value mappings,
declaring a new class can be overkill."

I do understand your point though -- people might not use Bunch in the
way it's intended. Of course, those same people can already do the same
thing with a dict instead (e.g. write a bunch of functions to handle a
certain type of dict). If someone wants to write Perl in Python,
there's not much we can really do to stop them...

Steve
Jul 18 '05 #9

P: n/a
Nick Coghlan wrote:
The proposed use cases sound more appropriate for a "named tuple" than
any sort of dictionary. (This may have been mentioned in previous
discussions. I wasn't keeping track of those, though)
For the return values, yeah, a "named tuple" is probably at least as
appropriate. I'm not sure a "named tuple" is necessary for the
hierarchical data. (It wasn't for me in my DOM to Bunch example.)

I saw the "named tuple" thread slowly die, mainly because the most ideal
solution:

(name1:val1, name2:val2)

requires a change in Python's syntax, which is a tough route to go.

This PEP isn't attempting to solve the "named tuple" problem, though if
that thread picks back up again and produces a solution that also solves
the problems here, I'm more than willing to merge the two PEPs.

Note that I'm not trying to solve all the problems that a "named tuple"
could solve -- just the problem of converting __getattr__ syntax to
dotted-attribute syntax without the need to declare a class.
Notice that I've used 'fromPairs' rather than 'fromMapping', since
consistent order matters for a tuple. Comparison semantics are inherited
directly from tuple, and don't care about names (they're only interested
in values).
frommapping was intended to handle the recursive (hierarchical data)
case. (Perhaps it needs a better name to clarify this...) The shallow
conversion was handled in the Bunch constructor. I don't see that your
named_tuple type handles the recursive case, does it?

Also, it seems like there has to be a better way to do the "opposite of
zip()" in fromPairs(), but I sure as hell can't think of it.


I think zip(*) is usually the inverse of zip():

..>>> zip(*sorted({'x':3, 'y':8}.items()))
..[('x', 'y'), (3, 8)]

Steve
Jul 18 '05 #10

P: n/a
Nick Craig-Wood wrote:
class Hash:
def __init__(self, **kwargs):
for key,value in kwargs.items():
setattr(self, key, value)
def __getitem__(self, x):
return getattr(self, x)
def __setitem__(self, x, y):
setattr(self, x, y)


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

--Scott David Daniels
Sc***********@Acm.Org
Jul 18 '05 #11

P: n/a
Scott David Daniels wrote:
Nick Craig-Wood wrote:
class Hash:
def __init__(self, **kwargs):
for key,value in kwargs.items():
setattr(self, key, value)
def __getitem__(self, x):
return getattr(self, x)
def __setitem__(self, x, y):
setattr(self, x, y)

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


Oh, I guess I should mention that Hash actually does something Bunch is
not intended to -- it supports __getitem__ style access in addition to
dotted-attribute (__getattr__) style access. Bunch is intended only to
support dotted-attribute style access, though it does support the
one-way conversion of a mapping object to a Bunch.

Steve
Jul 18 '05 #12

P: n/a
[Scott David Daniels]
You can simplify this:
class Hash(object):
def __init__(self, **kwargs):
for key,value in kwargs.items():
setattr(self, key, value)


Might it be:

def __init__(self, **kwargs):
self.__dict__.update(kwargs)

--
François Pinard http://pinard.progiciels-bpi.ca
Jul 18 '05 #13

P: n/a
Steven Bethard wrote:
Peter Otten wrote:
Steven Bethard wrote:
def __eq__(self, other):
"""x.__eq__(y) <==> x == y"""
return (isinstance(other, self.__class__)
and self.__dict__ == other.__dict__)
This results in an asymmetry:

[snip]

Whether this is intended, I don't know. If someone can enlighten me...

Unintended.
Oops, I meant CPython's rich comparison, not your __eq__() implementation.
I'll switch to
self.__class__ == other.__class__
or
type(self) == type(other)
Any preference?


Normally none of them. The former if hard pressed because all old-style
classes have the same type(). But it doesn't really matter here.

Peter

Jul 18 '05 #14

P: n/a
Peter Otten wrote:
Steven Bethard wrote:
def __eq__(self, other):
"""x.__eq__(y) <==> x == y"""
return (isinstance(other, self.__class__)
and self.__dict__ == other.__dict__)

This results in an asymmetry:
from bunch import Bunch
class B(Bunch): pass ...B().__eq__(Bunch()) FalseBunch().__eq__(B()) True

With indirect use of __eq__() this puzzling behaviour disappears:
B() == Bunch() FalseBunch() == B()

False

Whether this is intended, I don't know. If someone can enlighten me...


It does look like it's at least documented:

http://docs.python.org/ref/comparisons.html
"The operators <, >, ==, >=, <=, and != compare the values of two
objects. The objects need not have the same type. If both are numbers,
they are converted to a common type. Otherwise, objects of different
types always compare unequal, and are ordered consistently but arbitrarily."

This sounds like using "==" makes a guarantee that objects of different
types will compare unequal, while my __eq__ method (using isinstance)
did not make this guarantee.

I tried to check the C code to verify this (that different classes are
guaranteed to be unequal) but rich comparisons make that code pretty
complicated.

Steve
Jul 18 '05 #15

P: n/a

"Steven Bethard" <st************@gmail.com> wrote in message
def __eq__(self, other):
"""x.__eq__(y) <==> x == y"""
return (isinstance(other, self.__class__)
Since an instance of a subclass is an instance of a parent class, but not
vice versa, I believe you introduce here the assymetry you verify below.
and self.__dict__ == other.__dict__)

This results in an asymmetry:
>from bunch import Bunch
>class B(Bunch): pass

...
>B().__eq__(Bunch())

False
>Bunch().__eq__(B())

True


Terry J. Reedy

Jul 18 '05 #16

P: n/a
Terry Reedy wrote:
"Steven Bethard" <st************@gmail.com> wrote in message
>>>def __eq__(self, other): """x.__eq__(y) <==> x == y"""
return (isinstance(other, self.__class__)

Since an instance of a subclass is an instance of a parent class, but not
vice versa, I believe you introduce here the assymetry you verify below.


Yes, the asymmetry is due to isinstance.

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.

Steve
Jul 18 '05 #17

P: n/a
Steven Bethard <st************@gmail.com> wrote:
Nick Craig-Wood wrote:
Steven Bethard <st************@gmail.com> wrote:
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!
My experience from using this is that whenever I used Hash(), I found
that later on in the refinement of the conversion it became its own
class.


This has also generally been my experience, though I'm not sure it's as
true for the XML DOM to Bunch translation. Did you use Hash() in the
same way for hierarchical data?


Hash() got nested yes, but not in a general purpose structure like
your XML example.
So my take on the matter is that this encourages perl style
programming (just ram it in a hash, and write lots of functions acting
on it) rather than creating a specific class for the job which is dead
easy in python anyway and to which you can attach methods etc.


You'll note that the (pre-)PEP explicitly notes that this object is
intended only for use when no methods are associated with the attributes:

"When no methods are to be associated with the attribute-value mappings,
declaring a new class can be overkill."

I do understand your point though -- people might not use Bunch in the
way it's intended. Of course, those same people can already do the same
thing with a dict instead (e.g. write a bunch of functions to handle a
certain type of dict). If someone wants to write Perl in Python,
there's not much we can really do to stop them...


No there isn't ;-)

The above does make it a lot more convenient though blob['foo'] is
rather difficult to type compared to blob.foo!

--
Nick Craig-Wood <ni**@craig-wood.com> -- http://www.craig-wood.com/nick
Jul 18 '05 #18

P: n/a
Scott David Daniels <Sc***********@Acm.Org> wrote:
Nick Craig-Wood wrote:
class Hash:
def __init__(self, **kwargs):
for key,value in kwargs.items():
setattr(self, key, value)
def __getitem__(self, x):
return getattr(self, x)
def __setitem__(self, x, y):
setattr(self, x, y)


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...
class Hash(object): .... def __init__(self, **kwargs):
.... for key,value in kwargs.items():
.... setattr(self, key, value)
.... __getitem__ = getattr
.... __setitem__ = setattr
.... h=Hash(a=1,b=2)
h.a 1 h['a'] 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!
--
Nick Craig-Wood <ni**@craig-wood.com> -- http://www.craig-wood.com/nick
Jul 18 '05 #19

P: n/a
Nick Craig-Wood wrote:
Scott David Daniels <Sc***********@Acm.Org> wrote:
Nick Craig-Wood wrote:
> class Hash:
> def __init__(self, **kwargs):
> for key,value in kwargs.items():
> setattr(self, key, value)
> def __getitem__(self, x):
> return getattr(self, x)
> def __setitem__(self, x, y):
> setattr(self, x, y)


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...
class Hash(object): ... def __init__(self, **kwargs):
... for key,value in kwargs.items():
... setattr(self, key, value)
... __getitem__ = getattr
... __setitem__ = setattr
... h=Hash(a=1,b=2)
h.a 1 h['a'] 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!


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

http://mail.python.org/pipermail/pyt...ay/219424.html

for a strange example.

Peter
Jul 18 '05 #20

P: n/a
Steven Bethard wrote:
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.
Jul 18 '05 #21

P: n/a
Nick Craig-Wood wrote:
Scott David Daniels <Sc***********@Acm.Org> wrote:
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...
h['a']


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
Sc***********@Acm.Org
Jul 18 '05 #22

P: n/a
Steven Bethard wrote:
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.
Jul 18 '05 #23

P: n/a
Istvan Albert wrote:
Steven Bethard wrote:
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.


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
Jul 18 '05 #24

P: n/a
Steven Bethard <st************@gmail.com> writes:
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.

...
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.
Jul 18 '05 #25

P: n/a
Istvan Albert wrote:
Steven Bethard wrote:
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.


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
Jul 18 '05 #26

P: n/a
Steven Bethard <st************@gmail.com> writes:
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.

...
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.
Jul 18 '05 #27

P: n/a
Peter Otten wrote:
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 <ex*****@divmod.com> wrote: 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!

--
Nick Craig-Wood <ni**@craig-wood.com> -- http://www.craig-wood.com/nick
Jul 18 '05 #28

P: n/a
Peter Otten wrote:
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 <ex*****@divmod.com> wrote: 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!

--
Nick Craig-Wood <ni**@craig-wood.com> -- http://www.craig-wood.com/nick
Jul 18 '05 #29

P: n/a
Paul Rubin wrote:
Steven Bethard <st************@gmail.com> writes:
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.


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


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
--
http://www.holdenweb.com
http://pydish.holdenweb.com
Holden Web LLC +1 800 494 3119
Jul 18 '05 #30

P: n/a
Paul Rubin wrote:
Steven Bethard <st************@gmail.com> writes:
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.


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


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
--
http://www.holdenweb.com
http://pydish.holdenweb.com
Holden Web LLC +1 800 494 3119
Jul 18 '05 #31

P: n/a
On Fri, 03 Dec 2004 07:41:59 -0500, Steve Holden <st***@holdenweb.com> wrote:
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: ca********@gmail.com
mail: ca********@yahoo.com
Jul 18 '05 #32

P: n/a
Steven Bethard wrote:
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
Jul 18 '05 #33

P: n/a
Istvan Albert wrote:
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.

Jul 18 '05 #34

P: n/a
On Fri, 03 Dec 2004 08:58:48 -0500, Istvan Albert
<ia*****@mailblocks.com> wrote:
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: ca********@gmail.com
mail: ca********@yahoo.com
Jul 18 '05 #35

P: n/a
Istvan Albert wrote:
Steven Bethard wrote:
> 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):


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)

--
Ian Bicking / ia**@colorstudy.com / http://blog.ianbicking.org
Jul 18 '05 #36

P: n/a
In article <Qtqrd.177755$HA.59149@attbi_s01>,
Steven Bethard <st************@gmail.com> wrote:
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.
class EQ(object): .... def __eq__ (self, other):
.... return True
.... eq = EQ()
eq == 3 True 3 == eq True class NEQ(object): .... def __eq__ (self, other):
.... return False
.... neq=NEQ()
eq == neq True neq == eq False


Regards. Mel.
Jul 18 '05 #37

P: n/a
Mel Wilson wrote:
In article <Qtqrd.177755$HA.59149@attbi_s01>,
Steven Bethard <st************@gmail.com> wrote:
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:

[snip]

Check his example:

http://mail.python.org/pipermail/pyt...er/252660.html
Jul 18 '05 #38

P: n/a
Istvan Albert wrote:
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
Jul 18 '05 #39

P: n/a
Ian Bicking wrote:
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
Jul 18 '05 #40

P: n/a
Mel Wilson wrote:
In article <Qtqrd.177755$HA.59149@attbi_s01>,
Steven Bethard <st************@gmail.com> wrote:
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:

[snip]

Hmm... maybe it only shows up with subclassing?
class C(object): .... def __eq__(self, other):
.... return True
.... class D(C): .... def __eq__(self, other):
.... return False
.... c, d = C(), D()
c == 3 True 3 == c True c == d False d == c False c.__eq__(d) True d.__eq__(c)

False
STeve
Jul 18 '05 #41

P: n/a
Steven Bethard wrote:
Mel Wilson wrote:
In article <Qtqrd.177755$HA.59149@attbi_s01>,
Steven Bethard <st************@gmail.com> wrote:
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:


[snip]

Check his example:

http://mail.python.org/pipermail/pyt...er/252660.html


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.
Jul 18 '05 #42

P: n/a
Steven Bethard wrote:
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.
Jul 18 '05 #43

P: n/a
Istvan Albert wrote:
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
Jul 18 '05 #44

P: n/a
In article <zN6sd.505724$D255547@attbi_s51>,
Steven Bethard <st************@gmail.com> wrote:
Mel Wilson wrote:
In article <Qtqrd.177755$HA.59149@attbi_s01>,
Steven Bethard <st************@gmail.com> wrote:
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:

[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.
class Eq(object): .... def __eq__(self, other):
.... return True
.... class Neq(Eq): .... def __eq__(self, other):
.... print "(according to Neq)"
.... return False
.... eq,neq=Eq(),Neq()
eq==neq (according to Neq)
False neq==eq (according to Neq)
False


Regards. Mel.
















Jul 18 '05 #45

P: n/a
[Mel Wilson]
:) 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.
class Eq(object): ... def __eq__(self, other):
... return True
... class Neq(Eq): ... def __eq__(self, other):
... print "(according to Neq)"
... return False
... eq,neq=Eq(),Neq()
eq==neq (according to Neq)
False neq==eq (according to Neq)
False


See the Python (language, not library) reference manual, section 3.3.8
("Coercion rules"), bullet point starting with:

Exception to the previous item: if the left operand is an
instance of a built-in type or a new-style class, and the right
operand is an instance of a proper subclass of that type or
class, ...
Jul 18 '05 #46

P: n/a
Tim Peters wrote:
See the Python (language, not library) reference manual, section 3.3.8
("Coercion rules"), bullet point starting with:

Exception to the previous item: if the left operand is an
instance of a built-in type or a new-style class, and the right
operand is an instance of a proper subclass of that type or
class, ...


So that is settled then. Not the most likely place to investigate when one
has just read that "Arguments to rich comparison methods are never coerced"
in 3.3.1 ("Basic customization"), though.

Peter

Jul 18 '05 #47

P: n/a
Peter Otten wrote:
Tim Peters wrote:

See the Python (language, not library) reference manual, section 3.3.8
("Coercion rules"), bullet point starting with:

Exception to the previous item: if the left operand is an
instance of a built-in type or a new-style class, and the right
operand is an instance of a proper subclass of that type or
class, ...

So that is settled then. Not the most likely place to investigate when one
has just read that "Arguments to rich comparison methods are never coerced"
in 3.3.1 ("Basic customization"), though.


<Heh - just reread this, and realised my reply below misses the point. You're
right that the subsection heading is a little misleading. . .>

Nothing is getting coerced. It's just that "A binop B" gets translated to a
method call differently depending on the types of A and B. The options being:

A.__binop__(B) # 1 - the usual case
B.__binop__(A) # 2.1 - commutative op, and B is a proper subclass of A
B.__rbinop__(A) # 2.2 - possibly non-commutative op, and B is a proper subclass of A

This is so that things like the following work:

..>>> class SillyAdd(long):
..... __add__ = long.__mul__
..... __radd__ = __add__
.....
..>>> a = SillyAdd(4)
..>>> a
..4L
..>>> a + 5
..20L
..>>> 5 + a
..20L

Cheers,
Nick.
Jul 18 '05 #48

P: n/a
Steven Bethard wrote:
Ian Bicking wrote:
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?


class bunch(object):
def __init__(self, __seq=None, **kw):
if __seq is not None:
if hasattr(__seq, 'keys'):
for key in __seq:
setattr(self, key, __seq[key])
else:
for name, value in __seq:
setattr(self, name, value)
for name, value in kw.items():
setattr(self, name, value)

That should match dict.update, at least from the 2.4 help(dict.update).
I'm not sure that will work for updating from a bunch object; also,
bunch objects could have a 'keys' attribute without being dictionaries.
Do you get attributes from non-iterables through their __dict__? I
don't care for that at all. Are bunch objects iterable?

--
Ian Bicking / ia**@colorstudy.com / http://blog.ianbicking.org
Jul 18 '05 #49

P: n/a
In article <co*************@news.t-online.com>,
Peter Otten <__*******@web.de> wrote:
Tim Peters wrote:
See the Python (language, not library) reference manual, section 3.3.8
("Coercion rules"), bullet point starting with:

Exception to the previous item: if the left operand is an
instance of a built-in type or a new-style class, and the right
operand is an instance of a proper subclass of that type or
class, ...


So that is settled then. Not the most likely place to investigate when one
has just read that "Arguments to rich comparison methods are never coerced"
in 3.3.1 ("Basic customization"), though.


At some point Python will cease to be a simple language.

Regards. Mel.
Jul 18 '05 #50

This discussion thread is closed

Replies have been disabled for this discussion.