473,881 Members | 1,644 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

pre-PEP generic objects

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=Bunc h(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=Bu nch(lumberjack= True, spam=42), peng='shrub')
y = Bunch(peng='shr ub', parrot=Bunch(sp am=42, lumberjack=True ))
z = Bunch(parrot=Bu nch(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(elemen t): ... if not isinstance(elem ent, xml.dom.minidom .Element):
... raise TypeError('item s only retrievable from Elements')
... if element.attribu tes:
... for key, value in element.attribu tes.items():
... yield key, value
... children = {}
... for child in element.childNo des:
... if child.nodeType == xml.dom.minidom .Node.TEXT_NODE :
... text_list = children.setdef ault('text', [])
... text_list.appen d(child.nodeVal ue)
... else:
... children.setdef ault(child.node Name, []).append(
... Bunch.frommappi ng(child, getitems=getite ms))
... 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.frommappi ng(doc.document Element, getitems=getite ms)
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(egg s=1, spam=2), ham=3) Bunch(eggs=1, ham=3, spam=2)
"""

def __init__(self, *args, **kwds):
"""Initiali zes a Bunch instance."""
self.update(*ar gs, **kwds)

def __eq__(self, other):
"""x.__eq__ (y) <==> x == y"""
return (isinstance(oth er, 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__.i tems()))

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(othe r, self.__class__) :
other = other.__dict__
try:
self.__dict__.u pdate(other)
except TypeError:
raise TypeError('cann ot update Bunch with %s' %
type(other).__n ame__)
elif len(args) != 0:
raise TypeError('expe cted 1 argument, got %i' %
len(args))
self.__dict__.u pdate(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.frommappi ng({'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).i tems
except AttributeError:
getitems = _items
# build the Bunch from the mapping, recursively
result = cls()
for key, value in getitems(mappin g):
try:
value = cls.frommapping (value, getitems=getite ms)
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
49 2919
Mel Wilson wrote:
In article <Qtqrd.177755$H A.59149@attbi_s 01>,
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
Steven Bethard wrote:
Mel Wilson wrote:
In article <Qtqrd.177755$H A.59149@attbi_s 01>,
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
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
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
In article <zN6sd.505724$D 255547@attbi_s5 1>,
Steven Bethard <st************ @gmail.com> wrote:
Mel Wilson wrote:
In article <Qtqrd.177755$H A.59149@attbi_s 01>,
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
[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
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
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
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.updat e).
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
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 thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

21
10235
by: Headless | last post by:
I've marked up song lyrics with the <pre> tag because it seems the most appropriate type of markup for the type of data. This results in inefficient use of horizontal space due to UA's default rendering of <pre> in a fixed width font. To change that I'd have to specify a proportional font family, thereby falling into the size pitfall that is associated with any sort of author specified font family: a) If I specify a sans serif font...
3
3802
Pre
by: Neal | last post by:
A few questions about pre... When presenting preformatted text using the white-space: pre; property/value, Opera renders long lines at small viewport widths as exiting the borders of the element, IE6 extends the element to contain the line. Both necessitate horizontal scrolling. 1) Is one considered incorrect, or are both fair interpretations of the recommendation?
7
18545
by: Alan Illeman | last post by:
How do I set several different properties for PRE in a CSS stylesheet, rather than resorting to this: <BODY> <PRE STYLE="font-family:monospace; font-size:0.95em; width:40%; border:red 2px solid; color:red;
2
2800
by: Buck Turgidson | last post by:
I want to have a css with 2 PRE styles, one bold with large font, and another non-bold and smaller font. I am new to CSS (and not exactly an expert in HTML, for that matter). Is there a way to do this in CSS? <STYLE TYPE="text/css"> pre{ font-size:xx-large;
5
718
by: Michael Shell | last post by:
Greetings, Consider the XHTML document attached at the end of this post. When viewed under Firefox 1.0.5 on Linux, highlighting and pasting (into a text editor) the <pre> tag listing will preserve formatting (white space and line feeds). However, this is not true when doing the same with the <code> tag listing (it will all be pasted on one line with multiple successive spaces treated as a single space) despite the fact that...
8
3880
by: Jarno Suni not | last post by:
It seems to be invalid in HTML 4.01, but valid in XHTML 1.0. Why is there the difference? Can that pose a problem when such a XHTML document is served as text/html?
7
2757
by: Rocky Moore | last post by:
I have a web site called HintsAndTips.com. On this site people post tips using a very simply webform with a multi line TextBox for inputing the tip text. This text is encode to HTML so that no tags will remain making the page safe (I have to convert the linefeeds to <BR>s because the Server.EncodeHTML does not do that it seems). The problem is that users can use a special tag when editing the top to specify an area of the tip that will...
9
5561
by: Eric Lindsay | last post by:
I can't figure how to best display little snippets of shell script using <pre>. I just got around to organising to bulk validate some of my web pages, and one of the problems occurs with Bash shell pieces like this: <pre><code> #!/bin/sh ftp -i -n ftp.server.com&lt; &lt;EOF user username password epsv4 cd /
14
3648
by: Schraalhans Keukenmeester | last post by:
I am building a default sheet for my linux-related pages. Since many linux users still rely on/prefer viewing textmode and unstyled content I try to stick to the correct html tags to pertain good readibility on browsers w/o css-support. For important notes, warnings etc I use the <pre> tag, which shows in a neat bordered box when viewed with css, and depending on its class a clarifying background-image is shown. I would like the...
0
9928
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, weíll explore What is ONU, What Is Router, ONU & Routerís main usage, and What is the difference between ONU and Router. Letís take a closer look ! Part I. Meaning of...
0
11100
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10718
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
10816
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
10401
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
5781
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5977
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4597
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
3
3225
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.