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

3 Suggestions to Make Python Easier For Children

128 views
Skip to first unread message

Mark Summerfield

unread,
Aug 2, 2014, 2:45:14 AM8/2/14
to
Last week I spent a couple of days teaching two children (10 and 13 -- too big an age gap!) how to do some turtle graphics with Python. Neither had programmed Python before -- one is a Minecraft ace and the other had done Scratch.

Suggestion #1: Make IDLE start in the user's home directory.

Suggestion #2: Make all the turtle examples begin "from turtle import *" so no leading turtle. is needed in the examples.

Suggestion #3: Make object(key=value, ...) legal and equiv of types.SimpleNamespace(key=value, ...).

Explanation & Rationale (long):

They both had Windows laptops and so we began by installing Python. They didn't know if they had 32- or 64-bit computers but it didn't matter, the installs just worked. One child had no problem but the other ended up on the Download page which lists all the possible Windows downloads; so we had to go back and start again.

We discovered that IDLE's working directory is C:\Python34 -- surely this is the wrong working directory for 99% of normal users and for 100% of beginners? Naturally they saved all their .py files there until I realised. And then when I got them to create a new directory for their own files ("Python" and "pie_thon") and dragged them across they also managed to move python.exe and pythonw.exe.

Couldn't IDLE start in the home directory?

The turtle package is quite nice and there is a very short but dramatic colored star example at the start of the docs. There really ought to be a little gallery of similar examples, including circle, polygon, and star (yes I know circle and polygon are built in but it is nice for children to see a simple implementation), and of course a few more -- but not any that compose shapes (e.g., face and house), since one would want them to learn how to compose themselves.

Making them type turtle.this and turtle.that would be torture, so we began every file with

from turtle import *

I wish the doc examples did the same. (For randint we did from random import randint since only the one function was needed.)

I stuck to pure procedural programming since OO would have been too much cognitive load to add given the time. I did make sure that once they'd created something (e.g., a face), they then put it inside a function.

However, when it came to making a game ("hit the hippo") we needed to maintain some state that several functions could access. (I had to gloss over that we were doing higher order programming! -- e.g. onscreenclick(goto) etc.) What I wanted to write was this:

state = object(moving=True, score=0) # Illegal!
...
state.moving = False # etc.

But was forced to do this:

state = {'moving': True, 'score': 0}
...
state['moving'] = False # so they get confused between {} and []

There is an alternative:

from types import SimpleNamespace

state = SimpleNamespace(moving=True, score=0)
...
state.moving = False # etc.

But I felt that explaining what a namespace was would be too much -- remember they're having problems remembering to put () at the end of function calls etc.
It would be nicer if object() acted as it does now but object(key=value,...) behaved like types.SimpleNamespace.

Marko Rauhamaa

unread,
Aug 2, 2014, 3:14:08 AM8/2/14
to
Mark Summerfield <li...@qtrac.plus.com>:

> Suggestion #1: Make IDLE start in the user's home directory.
>
> Suggestion #2: Make all the turtle examples begin "from turtle import
> *" so no leading turtle. is needed in the examples.
>
> Suggestion #3: Make object(key=value, ...) legal and equiv of
> types.SimpleNamespace(key=value, ...).

Great suggestions for adults as well.

Expanding #3:

>>> o = object()
>>> o.x = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'x'

Why?


Marko

Mark Summerfield

unread,
Aug 2, 2014, 3:38:08 AM8/2/14
to
On Saturday, 2 August 2014 08:14:08 UTC+1, Marko Rauhamaa wrote:
> Mark Summerfield:
object() returns a minimal featureless object with no dictionary (no __dict__ attribute). This makes sense for efficiency since not all objects need a dictionary.

What I'm suggesting is that _in addition_ to the existing behavior, a new behavior is introduced whereby object() called with keyword arguments creates a types.SimpleNamespace object (i.e., a basic object that has a dictionary initialized by the keyword arguments).

Mark.

Mark Lawrence

unread,
Aug 2, 2014, 3:46:04 AM8/2/14
to pytho...@python.org
On 02/08/2014 07:45, Mark Summerfield wrote:
> Last week I spent a couple of days teaching two children (10 and 13 -- too big an age gap!) how to do some turtle graphics with Python. Neither had programmed Python before -- one is a Minecraft ace and the other had done Scratch.
>
> Suggestion #1: Make IDLE start in the user's home directory.

Entirely agree. Please raise an enhancement request on the bug tracker
if there isn't already one.

>
> Suggestion #2: Make all the turtle examples begin "from turtle import *" so no leading turtle. is needed in the examples.

I'm not so sure about this, but raise an enhancement request and see
what happens. Worst case it gets rejected, best case it gets accepted,
implemented and patch applied.

>
> Suggestion #3: Make object(key=value, ...) legal and equiv of types.SimpleNamespace(key=value, ...).

Haven't the faintest idea and too lazy to find out :) I suggest follow
advice from #2.

>
> Explanation & Rationale (long):
>
> They both had Windows laptops and so we began by installing Python. They didn't know if they had 32- or 64-bit computers but it didn't matter, the installs just worked. One child had no problem but the other ended up on the Download page which lists all the possible Windows downloads; so we had to go back and start again.
>
> We discovered that IDLE's working directory is C:\Python34 -- surely this is the wrong working directory for 99% of normal users and for 100% of beginners? Naturally they saved all their .py files there until I realised. And then when I got them to create a new directory for their own files ("Python" and "pie_thon") and dragged them across they also managed to move python.exe and pythonw.exe.
>
> Couldn't IDLE start in the home directory?
>
> The turtle package is quite nice and there is a very short but dramatic colored star example at the start of the docs. There really ought to be a little gallery of similar examples, including circle, polygon, and star (yes I know circle and polygon are built in but it is nice for children to see a simple implementation), and of course a few more -- but not any that compose shapes (e.g., face and house), since one would want them to learn how to compose themselves.
>
> Making them type turtle.this and turtle.that would be torture, so we began every file with
>
> from turtle import *
>
> I wish the doc examples did the same. (For randint we did from random import randint since only the one function was needed.)
>
> I stuck to pure procedural programming since OO would have been too much cognitive load to add given the time. I did make sure that once they'd created something (e.g., a face), they then put it inside a function.
>
> However, when it came to making a game ("hit the hippo") we needed to maintain some state that several functions could access. (I had to gloss over that we were doing higher order programming! -- e.g. onscreenclick(goto) etc.) What I wanted to write was this:
>
> state = object(moving=True, score=0) # Illegal!
> ...
> state.moving = False # etc.
>
> But was forced to do this:
>
> state = {'moving': True, 'score': 0}
> ...
> state['moving'] = False # so they get confused between {} and []
>
> There is an alternative:
>
> from types import SimpleNamespace
>
> state = SimpleNamespace(moving=True, score=0)
> ...
> state.moving = False # etc.
>
> But I felt that explaining what a namespace was would be too much -- remember they're having problems remembering to put () at the end of function calls etc.
> It would be nicer if object() acted as it does now but object(key=value,...) behaved like types.SimpleNamespace.
>

Finally I believe there's a very strong chance that of of this will be
taken on board. I've noticed activity on the bugtracker recently
regarding turtle, GSOC student maybe?

--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

Mark Summerfield

unread,
Aug 2, 2014, 4:03:02 AM8/2/14
to
On Saturday, 2 August 2014 08:46:04 UTC+1, Mark Lawrence wrote:
> On 02/08/2014 07:45, Mark Summerfield wrote:
>
[snip]
>
> > Suggestion #1: Make IDLE start in the user's home directory.
>
> Entirely agree. Please raise an enhancement request on the bug tracker
> if there isn't already one.

Done: http://bugs.python.org/issue22121

> > Suggestion #2: Make all the turtle examples begin "from turtle import *" so no leading turtle. is needed in the examples.
>
> I'm not so sure about this, but raise an enhancement request and see
> what happens. Worst case it gets rejected, best case it gets accepted,
> implemented and patch applied.

Done: http://bugs.python.org/issue22122

> > Suggestion #3: Make object(key=value, ...) legal and equiv of types.SimpleNamespace(key=value, ...).
>
> Haven't the faintest idea and too lazy to find out :) I suggest follow
> advice from #2.

Done: http://bugs.python.org/issue22123

Mark.

Marko Rauhamaa

unread,
Aug 2, 2014, 4:03:37 AM8/2/14
to
Mark Summerfield <li...@qtrac.plus.com>:

> object() returns a minimal featureless object with no dictionary (no
> __dict__ attribute). This makes sense for efficiency since not all
> objects need a dictionary.

__setattr__ could create __dict__ belatedly.


Marko

Steven D'Aprano

unread,
Aug 2, 2014, 11:43:18 AM8/2/14
to
Marko Rauhamaa wrote:

> Expanding #3:
>
> >>> o = object()
> >>> o.x = 3
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> AttributeError: 'object' object has no attribute 'x'
>
> Why?

There are two intended uses for object and its instances:

- as the base class for all other classes;

- instances can be used as featureless sentinel objects where
only identity matters.

If you need instances which carry state, then object is the wrong class.


--
Steven

Steven D'Aprano

unread,
Aug 2, 2014, 12:00:47 PM8/2/14
to
Are we designing Son Of PHP, or a sensible language? *wink*

If object.__setattr__ did this, then we're left with two equally horrible
choices:

- only object works this way, not subclasses of object, which violates
the Liskov substitution principle;

- all instances, of every type, work this way, which causes chaos and
destruction everywhere. Some of the consequences:

* Classes with __slots__ will stop working as expected.
* Immutable instances will no longer be immutable, since they can
have mutable state.
* The CPython interpreter shares immutable built-ins like ints
across multiple processes. Either CPython would have to stop
doing this (increasing memory requirements significantly), or
one process could reach across to another and mutate its ints.

Well, perhaps not *equally* horrible. Compared to the second alternative,
violating the LSP seems relatively benign.

There's also the implementation difficulty that instance.__dict__ is kept in
a slot (obviously, since it cannot be stored in the per instance
__dict__ !), which means that you cannot easily or practically expect to
dynamically add/or delete it from instances. Now I'm sure with sufficient
effort we could do so, but that's a lot of effort for negligible (or even
negative) gain.



--
Steven

Marko Rauhamaa

unread,
Aug 2, 2014, 1:07:48 PM8/2/14
to
Steven D'Aprano <steve+comp....@pearwood.info>:

> Marko Rauhamaa wrote:
>> __setattr__ could create __dict__ belatedly.
>
> Are we designing Son Of PHP, or a sensible language? *wink*
>
> If object.__setattr__ did this, then we're left with two equally
> horrible choices:

Not a huge issue. Only mildly annoying to have to create:

class Object: pass

in every application.

And the newest Python releases let you replace that with:

import types
Object = types.SimpleNamespace


Marko

Mark Lawrence

unread,
Aug 2, 2014, 1:29:26 PM8/2/14
to pytho...@python.org
With the latter being part of suggestion #3 in the original post.

Marko Rauhamaa

unread,
Aug 2, 2014, 2:33:46 PM8/2/14
to
Mark Lawrence <bream...@yahoo.co.uk>:

> On 02/08/2014 18:07, Marko Rauhamaa wrote:
>> And the newest Python releases let you replace that with:
>>
>> import types
>> Object = types.SimpleNamespace
>
> With the latter being part of suggestion #3 in the original post.

Not quite. Even though Sugg. #3 would allow me to do:

object(x=3)

this wouldn't work:

object().x = 3

However, these would continue working:

types.SimpleNamespace(x=3)
types.SimpleNamespace().x = 3

Also, assuming Sugg. #3, would this work:

object(x=3).y = 2

as this does:

types.SimpleNamespace(x=3).y = 2


Marko

Ben Finney

unread,
Aug 2, 2014, 3:58:59 PM8/2/14
to pytho...@python.org
Steven D'Aprano <steve+comp....@pearwood.info> writes:

> If you need instances which carry state, then object is the wrong
> class.

Right. The ‘types’ module provides a SimpleNamespace class for the
common “bag of attributes” use case::

>>> import types
>>> foo = types.SimpleNamespace()
>>> foo.x = 3
>>> foo
namespace(x=3)

<URL:https://docs.python.org/3/library/types.html#types.SimpleNamespace>

--
\ “Nothing is more sacred than the facts.” —Sam Harris, _The End |
`\ of Faith_, 2004 |
_o__) |
Ben Finney

Mark Summerfield

unread,
Aug 2, 2014, 4:46:04 PM8/2/14
to
On Saturday, 2 August 2014 20:58:59 UTC+1, Ben Finney wrote:
> Steven D'Aprano writes:
>
> > If you need instances which carry state, then object is the wrong
> > class.

Fair enough.

> Right. The 'types' module provides a SimpleNamespace class for the
> common "bag of attributes" use case::
>
> >>> import types
> >>> foo = types.SimpleNamespace()
> >>> foo.x = 3
> >>> foo
> namespace(x=3)

This is too much for children (& beginners).

But perhaps what I should be asking for is for a new built-in that does what types.SimpleNamespace() does, so that without any import you can write, say,

foo = namespace(a=1, b=2)
# or
bar = namespace()
bar.a = 1

where under the hood namespace has the same behavior as types.SimpleNamespace().

Naturally, I understand that adding a new name is a big deal and may be too much to ask for beginners.

Chris Angelico

unread,
Aug 2, 2014, 5:05:39 PM8/2/14
to pytho...@python.org
On Sun, Aug 3, 2014 at 6:46 AM, Mark Summerfield <li...@qtrac.plus.com> wrote:
> But perhaps what I should be asking for is for a new built-in that does what types.SimpleNamespace() does, so that without any import you can write, say,
>
> foo = namespace(a=1, b=2)
> # or
> bar = namespace()
> bar.a = 1
>
> where under the hood namespace has the same behavior as types.SimpleNamespace().
>
> Naturally, I understand that adding a new name is a big deal and may be too much to ask for beginners.

This is where you might want to consider putting some imports into
site.py. That way, you can set up your own customized Python, without
waiting for changes to be approved for core (which they probably won't
- new builtins have a high requirement for necessity, not just "I
don't want to have to type import").

ChrisA

Mark Lawrence

unread,
Aug 2, 2014, 5:16:17 PM8/2/14
to pytho...@python.org
On 02/08/2014 22:05, Chris Angelico wrote:
> On Sun, Aug 3, 2014 at 6:46 AM, Mark Summerfield <li...@qtrac.plus.com> wrote:
>> But perhaps what I should be asking for is for a new built-in that does what types.SimpleNamespace() does, so that without any import you can write, say,
>>
>> foo = namespace(a=1, b=2)
>> # or
>> bar = namespace()
>> bar.a = 1
>>
>> where under the hood namespace has the same behavior as types.SimpleNamespace().
>>
>> Naturally, I understand that adding a new name is a big deal and may be too much to ask for beginners.
>
> This is where you might want to consider putting some imports into
> site.py. That way, you can set up your own customized Python, without
> waiting for changes to be approved for core (which they probably won't
> - new builtins have a high requirement for necessity, not just "I
> don't want to have to type import").
>
> ChrisA
>

I'd forgotten all about site.py so went to the 3.4.1 docs and found
"Deprecated since version 3.4: Support for the �site-python� directory
will be removed in 3.5.".

Plan B? :)

Chris Angelico

unread,
Aug 2, 2014, 5:24:35 PM8/2/14
to pytho...@python.org
On Sun, Aug 3, 2014 at 7:16 AM, Mark Lawrence <bream...@yahoo.co.uk> wrote:
> I'd forgotten all about site.py so went to the 3.4.1 docs and found
> "Deprecated since version 3.4: Support for the “site-python” directory will
> be removed in 3.5.".
>
> Plan B? :)

Oh. Hrm. I've no idea... but I'm sure there'll be a way to say
"execute this at the beginning of an interactive session". Dig in the
docs.

ChrisA

Mark Lawrence

unread,
Aug 2, 2014, 5:23:12 PM8/2/14
to pytho...@python.org
On 02/08/2014 22:16, Mark Lawrence wrote:
> On 02/08/2014 22:05, Chris Angelico wrote:
>> On Sun, Aug 3, 2014 at 6:46 AM, Mark Summerfield <li...@qtrac.plus.com>
>> wrote:
>>> But perhaps what I should be asking for is for a new built-in that
>>> does what types.SimpleNamespace() does, so that without any import
>>> you can write, say,
>>>
>>> foo = namespace(a=1, b=2)
>>> # or
>>> bar = namespace()
>>> bar.a = 1
>>>
>>> where under the hood namespace has the same behavior as
>>> types.SimpleNamespace().
>>>
>>> Naturally, I understand that adding a new name is a big deal and may
>>> be too much to ask for beginners.
>>
>> This is where you might want to consider putting some imports into
>> site.py. That way, you can set up your own customized Python, without
>> waiting for changes to be approved for core (which they probably won't
>> - new builtins have a high requirement for necessity, not just "I
>> don't want to have to type import").
>>
>> ChrisA
>>
>
> I'd forgotten all about site.py so went to the 3.4.1 docs and found
> "Deprecated since version 3.4: Support for the �site-python� directory
> will be removed in 3.5.".
>
> Plan B? :)
>

Plan B is revert to plan A, I didn't read things anything like
thoroughly enough, sorry about the noise :(

Terry Reedy

unread,
Aug 2, 2014, 7:12:28 PM8/2/14
to pytho...@python.org
On 8/2/2014 3:46 AM, Mark Lawrence wrote:
> On 02/08/2014 07:45, Mark Summerfield wrote:
>> Last week I spent a couple of days teaching two children (10 and 13 --
>> too big an age gap!) how to do some turtle graphics with Python.
>> Neither had programmed Python before -- one is a Minecraft ace and the
>> other had done Scratch.
>>
>> Suggestion #1: Make IDLE start in the user's home directory.
>
> Entirely agree. Please raise an enhancement request on the bug tracker
> if there isn't already one.
>
>>
>> Suggestion #2: Make all the turtle examples begin "from turtle import
>> *" so no leading turtle. is needed in the examples.
>
> I'm not so sure about this, but raise an enhancement request and see
> what happens. Worst case it gets rejected, best case it gets accepted,
> implemented and patch applied.
>
>>
>> Suggestion #3: Make object(key=value, ...) legal and equiv of
>> types.SimpleNamespace(key=value, ...).
>
> Haven't the faintest idea and too lazy to find out :) I suggest follow
> advice from #2.

Mark L: I think that redirecting discussion to the tracker a hour after
these ideas were posted, certainly for #2 and #3, which you were
rightfully unsure about, was a bit premature. Enhancement ideas
generally benefit from being cooked a bit longer on one or another
discussion list.

--
Terry Jan Reedy

Devin Jeanpierre

unread,
Aug 2, 2014, 7:18:07 PM8/2/14
to Chris Angelico, pytho...@python.org
On Sat, Aug 2, 2014 at 2:05 PM, Chris Angelico <ros...@gmail.com> wrote:
> On Sun, Aug 3, 2014 at 6:46 AM, Mark Summerfield <li...@qtrac.plus.com> wrote:
>> Naturally, I understand that adding a new name is a big deal and may be too much to ask for beginners.
>
> This is where you might want to consider putting some imports into
> site.py. That way, you can set up your own customized Python, without
> waiting for changes to be approved for core (which they probably won't
> - new builtins have a high requirement for necessity, not just "I
> don't want to have to type import").

Teaching a beginner a new programming language, but pretending it's
Python, doesn't sound like a fantastic idea.

-- Devin

Terry Reedy

unread,
Aug 2, 2014, 7:14:46 PM8/2/14
to pytho...@python.org
On 8/2/2014 4:03 AM, Mark Summerfield wrote:
> On Saturday, 2 August 2014 08:46:04 UTC+1, Mark Lawrence wrote:
>> On 02/08/2014 07:45, Mark Summerfield wrote:

Summarizing my responses on the tracker...

>>> Suggestion #1: Make IDLE start in the user's home directory.
>>
>> Entirely agree. Please raise an enhancement request on the bug tracker
>> if there isn't already one.
>
> Done: http://bugs.python.org/issue22121

As modified to fit existing behavior, this is a good idea and something
will probably happen eventually.

>>> Suggestion #2: Make all the turtle examples begin "from turtle import *" so no leading turtle. is needed in the examples.
>>
>> I'm not so sure about this, but raise an enhancement request and see
>> what happens. Worst case it gets rejected, best case it gets accepted,
>> implemented and patch applied.
>
> Done: http://bugs.python.org/issue22122

I suggest that the existing doc should be clarified instead.

>>> Suggestion #3: Make object(key=value, ...) legal and equiv of types.SimpleNamespace(key=value, ...).
>>
>> Haven't the faintest idea and too lazy to find out :) I suggest follow
>> advice from #2.
>
> Done: http://bugs.python.org/issue22123

This idea would mask bugs and is not necessary for the goal of making
types.SimpleNamespace more accessible. It should have had more
discussion here or on python-ideas.

--
Terry Jan Reedy

Chris Angelico

unread,
Aug 2, 2014, 7:27:18 PM8/2/14
to pytho...@python.org
On Sun, Aug 3, 2014 at 9:18 AM, Devin Jeanpierre <jeanpi...@gmail.com> wrote:
> On Sat, Aug 2, 2014 at 2:05 PM, Chris Angelico <ros...@gmail.com> wrote:
>> On Sun, Aug 3, 2014 at 6:46 AM, Mark Summerfield <li...@qtrac.plus.com> wrote:
>>> Naturally, I understand that adding a new name is a big deal and may be too much to ask for beginners.
>>
>> This is where you might want to consider putting some imports into
>> site.py. That way, you can set up your own customized Python, without
>> waiting for changes to be approved for core (which they probably won't
>> - new builtins have a high requirement for necessity, not just "I
>> don't want to have to type import").
>
> Teaching a beginner a new programming language, but pretending it's
> Python, doesn't sound like a fantastic idea.

It would still be Python, you just teach that certain lines just
always go at the top of your scripts (like "from __future__ import
print_function" if you're on Py2).

ChrisA

Ian Kelly

unread,
Aug 2, 2014, 8:59:44 PM8/2/14
to Python
from types import SimpleNamespace as namespace

Just have them put that at the top of each file, and tell them not to
worry about what it does.

Terry Reedy

unread,
Aug 2, 2014, 10:40:43 PM8/2/14
to pytho...@python.org
On 8/2/2014 5:16 PM, Mark Lawrence wrote:
> On 02/08/2014 22:05, Chris Angelico wrote:
>> On Sun, Aug 3, 2014 at 6:46 AM, Mark Summerfield <li...@qtrac.plus.com>
>> wrote:
>>> But perhaps what I should be asking for is for a new built-in that
>>> does what types.SimpleNamespace() does, so that without any import
>>> you can write, say,
>>>
>>> foo = namespace(a=1, b=2)
>>> # or
>>> bar = namespace()
>>> bar.a = 1
>>>
>>> where under the hood namespace has the same behavior as
>>> types.SimpleNamespace().
>>>
>>> Naturally, I understand that adding a new name is a big deal and may
>>> be too much to ask for beginners.
>>
>> This is where you might want to consider putting some imports into
>> site.py. That way, you can set up your own customized Python, without
>> waiting for changes to be approved for core (which they probably won't
>> - new builtins have a high requirement for necessity, not just "I
>> don't want to have to type import").
>>
>> ChrisA
>>
>
> I'd forgotten all about site.py so went to the 3.4.1 docs and found
> "Deprecated since version 3.4: Support for the “site-python” directory
> will be removed in 3.5.".

site-python is *nix only and differetn from site.py, which will not go
away. I believe PYTHONSTARTUP is also cross platform.


--
Terry Jan Reedy


Terry Reedy

unread,
Aug 2, 2014, 10:43:53 PM8/2/14
to pytho...@python.org
On 8/2/2014 8:59 PM, Ian Kelly wrote:
> On Sat, Aug 2, 2014 at 2:46 PM, Mark Summerfield <li...@qtrac.plus.com> wrote:
> from types import SimpleNamespace as namespace
>
> Just have them put that at the top of each file, and tell them not to
> worry about what it does.

In fact, for the original usecase, put it in a 'template file' that also
includes a turtle import. For one of my projects, I have @template with
several lines of boilerplate that I use for new files.


--
Terry Jan Reedy

Albert-Jan Roskam

unread,
Aug 3, 2014, 5:17:44 AM8/3/14
to Terry Reedy, pytho...@python.org
--- Original Message -----
> From: Terry Reedy <tjr...@udel.edu>
> To: pytho...@python.org
> Cc:
> Sent: Sunday, August 3, 2014 4:43 AM
> Subject: Re: Correct type for a simple "bag of attributes" namespace object
>
> On 8/2/2014 8:59 PM, Ian Kelly wrote:
>> On Sat, Aug 2, 2014 at 2:46 PM, Mark Summerfield
> <li...@qtrac.plus.com> wrote:
>>> On Saturday, 2 August 2014 20:58:59 UTC+1, Ben Finney  wrote:
>>>> Steven D'Aprano writes:
>>>>
>>>>> If you need instances which carry state, then object is the
> wrong
>>>>> class.
>>>
>>> Fair enough.
>>>
>>>> Right. The 'types' module provides a SimpleNamespace class
> for the
>>>> common "bag of attributes" use case::
>>>>
>>>>       >>> import types
>>>>       >>> foo = types.SimpleNamespace()
>>>>       >>> foo.x = 3
>>>>       >>> foo
>>>>       namespace(x=3)
>>>
>>> This is too much for children (& beginners).
>>>
>>> But perhaps what I should be asking for is for a new built-in that does
> what types.SimpleNamespace() does, so that without any import you can write,
> say,
>>>
>>> foo = namespace(a=1, b=2)
>>> # or
>>> bar = namespace()
>>> bar.a = 1

I find the following obscure (to me at least) use of type() useful exactly for this "bag of attributes" use case:
>>> employee = type("Employee", (object,), {})
>>> employee.name = "John Doe"
>>> employee.position = "Python programmer"
>>> employee.name, employee.position, employee
('John Doe', 'Python programmer', <class '__main__.Employee'>)

>>> details = dict(name="John Doe", position="Python programmer")
>>> employee = type("Employee", (object,), details)
>>> employee.name, employee.position, employee
('John Doe', 'Python programmer', <class '__main__.Employee'>)


regards,
Albert-Jan

Peter Otten

unread,
Aug 3, 2014, 5:37:04 AM8/3/14
to pytho...@python.org
Albert-Jan Roskam wrote:

> I find the following obscure (to me at least) use of type() useful exactly
> for this "bag of attributes" use case:
>>>> employee = type("Employee", (object,), {})
>>>> employee.name = "John Doe"
>>>> employee.position = "Python programmer"
>>>> employee.name, employee.position, employee
> ('John Doe', 'Python programmer', <class '__main__.Employee'>)

Are you sure you know what you are doing? The above is equivalent to

>>> class employee:
... name = "John Doe"
... position = "Python programmer"
...
>>> employee.name, employee.position, employee
('John Doe', 'Python programmer', <class '__main__.employee'>)
>>> type(employee)
<class 'type'>

Basically you are using classes as instances. While there is no fundamental
difference between classes and instances in Python you'll surprise readers
of your code and waste some space:

>>> import sys
>>> sys.getsizeof(employee)
976
>>> class Employee: pass
...
>>> employee = Employee()
>>> employee.name = "John Doe"
>>> employee.position = "Python programmer"
>>> sys.getsizeof(employee)
64


Albert-Jan Roskam

unread,
Aug 3, 2014, 6:51:12 AM8/3/14
to Peter Otten, pytho...@python.org

----- Original Message -----

> From: Peter Otten <__pet...@web.de>
> To: pytho...@python.org
> Cc:
> Sent: Sunday, August 3, 2014 11:37 AM
> Subject: Re: Correct type for a simple "bag of attributes" namespace object
>

> Albert-Jan Roskam wrote:
>
>> I find the following obscure (to me at least) use of type() useful exactly

>> for this "bag of attributes" use case:


>>>>> employee = type("Employee", (object,), {})
>>>>> employee.name = "John Doe"
>>>>> employee.position = "Python programmer"
>>>>> employee.name, employee.position, employee
>> ('John Doe', 'Python programmer', <class
> '__main__.Employee'>)
>
> Are you sure you know what you are doing? The above is equivalent to
>
>>>> class employee:
> ...    name = "John Doe"
> ...    position = "Python programmer"
> ...
>>>> employee.name, employee.position, employee
> ('John Doe', 'Python programmer', <class
> '__main__.employee'>)
>>>> type(employee)
> <class 'type'>
>
> Basically you are using classes as instances. While there is no fundamental
> difference between classes and instances in Python you'll surprise readers
> of your code and waste some space:

Yes, I know that it is equivalent, but I have always found it kind of ugly to use class() just to bundle a number of items. Like you are 'announcing OOP' (not sure how to put this into words), and then it's merely a simple bundle. Or maybe it's just me being silly, because it's even in the Python tutorial: https://docs.python.org/2/tutorial/classes.html#odds-and-ends


 
>>>> import sys
>>>> sys.getsizeof(employee)
> 976
>>>> class Employee: pass
> ...
>>>> employee = Employee()
>>>> employee.name = "John Doe"
>>>> employee.position = "Python programmer"
>>>> sys.getsizeof(employee)
> 64

Wow, I was not aware of that at all. So they are not equivalent after all.

Albert-Jan Roskam

unread,
Aug 3, 2014, 7:14:12 AM8/3/14
to Albert-Jan Roskam, Terry Reedy, pytho...@python.org

----- Original Message -----

> From: Albert-Jan Roskam <fo...@yahoo.com.dmarc.invalid>
> To: Terry Reedy <tjr...@udel.edu>; "pytho...@python.org" <pytho...@python.org>
> Cc:
> Sent: Sunday, August 3, 2014 11:17 AM
> Subject: Re: Correct type for a simple "bag of attributes" namespace object

<snip>

>>>>>   Right. The 'types' module provides a SimpleNamespace
> class
>> for the
>>>>>   common "bag of attributes" use case::
>>>>>
>>>>>       >>> import types
>>>>>       >>> foo = types.SimpleNamespace()
>>>>>       >>> foo.x = 3
>>>>>       >>> foo
>>>>>       namespace(x=3)
>>>>
>>>>   This is too much for children (& beginners).
>>>>
>>>>   But perhaps what I should be asking for is for a new built-in that
> does
>> what types.SimpleNamespace() does, so that without any import you can
> write,
>> say,
>>>>
>>>>   foo = namespace(a=1, b=2)
>>>>   # or
>>>>   bar = namespace()
>>>>   bar.a = 1
>

> I find the following obscure (to me at least) use of type() useful exactly for
> this "bag of attributes" use case:
>>>> employee = type("Employee", (object,), {})
>>>> employee.name = "John Doe"
>>>> employee.position = "Python programmer"
>>>> employee.name, employee.position, employee
> ('John Doe', 'Python programmer', <class
> '__main__.Employee'>)
>

>>>> details = dict(name="John Doe", position="Python
> programmer")

>>>> employee = type("Employee", (object,), details)


>>>> employee.name, employee.position, employee
> ('John Doe', 'Python programmer', <class
> '__main__.Employee'>)


PS to my previous mail: class() can (should?) be used here to do the exact same thing but it feels a little like "Getting your car [OOP] just because you need an ashtray [bundled items]". :-)

Peter Otten

unread,
Aug 3, 2014, 7:23:42 AM8/3/14
to pytho...@python.org
Albert-Jan Roskam wrote:

>
>
> ----- Original Message -----
>
>> From: Peter Otten <__pet...@web.de>
>> To: pytho...@python.org
>> Cc:
>> Sent: Sunday, August 3, 2014 11:37 AM
>> Subject: Re: Correct type for a simple "bag of attributes" namespace
>> object
>>
>> Albert-Jan Roskam wrote:
>>
>>> I find the following obscure (to me at least) use of type() useful
>>> exactly for this "bag of attributes" use case:
>>>>>> employee = type("Employee", (object,), {})
>>>>>> employee.name = "John Doe"
>>>>>> employee.position = "Python programmer"
>>>>>> employee.name, employee.position, employee
>>> ('John Doe', 'Python programmer', <class
>> '__main__.Employee'>)
>>
>> Are you sure you know what you are doing? The above is equivalent to
>>
>>>>> class employee:
>> ... name = "John Doe"
>> ... position = "Python programmer"
>> ...
>>>>> employee.name, employee.position, employee
>> ('John Doe', 'Python programmer', <class
>> '__main__.employee'>)
>>>>> type(employee)
>> <class 'type'>
>>
>> Basically you are using classes as instances. While there is no
>> fundamental difference between classes and instances in Python you'll
>> surprise readers of your code and waste some space:
>
> Yes, I know that it is equivalent, but I have always found it kind of ugly
> to use class() just to bundle a number of items.

But that's what you are doing, you just spell it differently.

> Like you are 'announcing
> OOP' (not sure how to put this into words), and then it's merely a simple
> bundle. Or maybe it's just me being silly, because it's even in the Python
> tutorial: https://docs.python.org/2/tutorial/classes.html#odds-and-ends
>
>>>>> import sys
>>>>> sys.getsizeof(employee)
>> 976
>>>>> class Employee: pass
>> ...
>>>>> employee = Employee()
>>>>> employee.name = "John Doe"
>>>>> employee.position = "Python programmer"
>>>>> sys.getsizeof(employee)
>> 64
> Wow, I was not aware of that at all. So they are not equivalent after all.

I think you are misunderstanding what I was trying to say; so to be
explicit:

>>> class Employee: pass
...
>>> sys.getsizeof(Employee)
976

i. e. you have a per-class and per-instance memory consumption. The latter
is smaller, so with regards to memory consumption instantiating only pays
off when there is more than one employee.

Mark Lawrence

unread,
Aug 3, 2014, 8:28:58 AM8/3/14
to pytho...@python.org
On 02/08/2014 20:58, Ben Finney wrote:
> Steven D'Aprano <steve+comp....@pearwood.info> writes:
>
>> If you need instances which carry state, then object is the wrong
>> class.
>
> Right. The ‘types’ module provides a SimpleNamespace class for the
> common “bag of attributes” use case::
>
> >>> import types
> >>> foo = types.SimpleNamespace()
> >>> foo.x = 3
> >>> foo
> namespace(x=3)
>
> <URL:https://docs.python.org/3/library/types.html#types.SimpleNamespace>
>

What Alex Martelli called a bunch?

http://code.activestate.com/recipes/52308-the-simple-but-handy-collector-of-a-bunch-of-named/

Roy Smith

unread,
Aug 3, 2014, 8:40:48 AM8/3/14
to
In article <mailman.12580.1407068...@python.org>,
Mark Lawrence <bream...@yahoo.co.uk> wrote:

> On 02/08/2014 20:58, Ben Finney wrote:
> > Steven D'Aprano <steve+comp....@pearwood.info> writes:
> >
> >> If you need instances which carry state, then object is the wrong
> >> class.
> >
> > Right. The ‘types’ module provides a SimpleNamespace class for the
> > common “bag of attributes” use case::
> >
> > >>> import types
> > >>> foo = types.SimpleNamespace()
> > >>> foo.x = 3
> > >>> foo
> > namespace(x=3)
> >
> > <URL:https://docs.python.org/3/library/types.html#types.SimpleNamespace>
> >
>
> What Alex Martelli called a bunch?
>
> http://code.activestate.com/recipes/52308-the-simple-but-handy-collector-of-a-
> bunch-of-named/

Still overkill :-) I usually just do:

class Data:
pass
my_obj = Data()

That's all you really need. It's annoying that you can't just do:

my_obj = object()

which would be even simpler, because (for reasons I don't understand),
you can't create new attributes on my_obj.

Chris Angelico

unread,
Aug 3, 2014, 8:56:37 AM8/3/14
to pytho...@python.org
On Sun, Aug 3, 2014 at 10:40 PM, Roy Smith <r...@panix.com> wrote:
> I usually just do:
>
> class Data:
> pass
> my_obj = Data()
>
> That's all you really need. It's annoying that you can't just do:
>
> my_obj = object()
>
> which would be even simpler, because (for reasons I don't understand),
> you can't create new attributes on my_obj.

Python 3.4 on Windows (32-bit):
>>> import sys
>>> class Data: pass
>>> sys.getsizeof(Data())
32
>>> sys.getsizeof(object())
8

Even before you add a single attribute, the object is four times the
size it needs to be. When you need a sentinel (for a function's
default argument, for instance), you don't need any attributes on it -
all you need is some object whose identity can be tested. And the
excess would be multiplied by a fairly large number of objects in the
system (it's not just object() that doesn't take attributes). If you
want a bag of attributes, get a bag of attributes, don't expect
object() to be it :) The only reason I can think of for expecting a
basic object to work this way is because of the parallel with
ECMAScript; it's not a fundamental part of the type hierarchy.

ChrisA

Roy Smith

unread,
Aug 3, 2014, 9:25:53 AM8/3/14
to
In article <mailman.12581.1407070...@python.org>,
Chris Angelico <ros...@gmail.com> wrote:

> On Sun, Aug 3, 2014 at 10:40 PM, Roy Smith <r...@panix.com> wrote:
> > I usually just do:
> >
> > class Data:
> > pass
> > my_obj = Data()
> >
> > That's all you really need. It's annoying that you can't just do:
> >
> > my_obj = object()
> >
> > which would be even simpler, because (for reasons I don't understand),
> > you can't create new attributes on my_obj.
>
> [...]
> The only reason I can think of for expecting a
> basic object to work this way is because of the parallel with
> ECMAScript; it's not a fundamental part of the type hierarchy.

I don't know about that. I agree that your explanation about object
size makes sense for why it wasn't built to work that way, but I don't
think the expectation should be surprising. When I write:

class Foo(Bar):
# stuff

I'm saying, "make Foos be just like Bars, except for this stuff". I can
do:

>>> class Foo(object):
... pass
...
>>> f = Foo()
>>> f.x = 1

in which case, I've said, "make Foos just like objects, except for, oh,
never mind, there aren't any differences". But, in reality, the system
bolted on the ability to have user-defined attributes without telling
me. I don't think it's unreasonable to be surprised at that.

Chris Angelico

unread,
Aug 3, 2014, 9:35:18 AM8/3/14
to pytho...@python.org
On Sun, Aug 3, 2014 at 11:25 PM, Roy Smith <r...@panix.com> wrote:
> in which case, I've said, "make Foos just like objects, except for, oh,
> never mind, there aren't any differences". But, in reality, the system
> bolted on the ability to have user-defined attributes without telling
> me. I don't think it's unreasonable to be surprised at that.

I agree that this is slightly surprising. However, imagine if it were
the other way:

class Foo(object):
x = 1
def __init__(self): self.y = 2

These would throw errors, unless you explicitly disable __slots__
processing. When there's two ways to do things and both would be
surprising, you pick the one that's going to be less of a surprise, or
surprising less often, and accept it. That doesn't mean it's not a
problem, but it's better than the alternative.

ChrisA

Akira Li

unread,
Aug 3, 2014, 10:22:09 AM8/3/14
to pytho...@python.org
Albert-Jan Roskam <fo...@yahoo.com.dmarc.invalid> writes:

> I find the following obscure (to me at least) use of type() useful
> exactly for this "bag of attributes" use case:
>>>> employee = type("Employee", (object,), {})
>>>> employee.name = "John Doe"
>>>> employee.position = "Python programmer"

You could write it as:

class Employee:
name = "Johh Doe"
position = "Python programmer"

It also makes it clear that `type()` returns a *class Employee*, not its
instance (Employee()) and therefore name, position are class attributes.


--
Akira

Roy Smith

unread,
Aug 3, 2014, 10:36:23 AM8/3/14
to
In article <mailman.12582.1407072...@python.org>,
I'm not following you at all. What does "the other way" mean, and how
would that cause the above to generate errors, and what does this have
to do with __slots__?

Marko Rauhamaa

unread,
Aug 3, 2014, 10:51:10 AM8/3/14
to
Peter Otten <__pet...@web.de>:

> i. e. you have a per-class and per-instance memory consumption. The
> latter is smaller, so with regards to memory consumption instantiating
> only pays off when there is more than one employee.

I've reached a point where I think classes are a superfluous OO concept.
You only need objects.

Python is close to that reality. The "class" keyword really creates a
function that creates objects, and the objects' class membership is
ignored in ducktyping.

Classes may or may not save RAM, but that is rather a low-brow point of
view toward OO.


Marko

Chris Angelico

unread,
Aug 3, 2014, 11:00:35 AM8/3/14
to pytho...@python.org
Fact #1: Some classes, like object, don't have a dictionary for
arbitrary attributes. (I'll call this __slots__ mode, although it's
not technically __slots__when it's a C-implemented class.)

Fact #2: You can subclass such objects.

There are two ways that this subclassing could be done. Either it
maintains __slots__ mode, which means you can see every change (and
going "class Foo(Bar): pass" will make an exact subclass of Bar with
identical behaviour), or it drops __slots__ and adds a dictionary
unless you explicitly reenable __slots__.

>>> class Base(object): __slots__ = ('a', 'b', 'c')
>>> class Deriv(Base): pass
>>> Base().d = 1
Traceback (most recent call last):
File "<pyshell#71>", line 1, in <module>
Base().d = 1
AttributeError: 'Base' object has no attribute 'd'
>>> Deriv().d = 1

Python opted to go with the second behaviour: the subclass is not
bound to the superclass's __slots__, but gets a dictionary unless it
itself specifies __slots__ (in which case it gets all of them,
parent's included):

>>> class SlottedDeriv(Base): __slots__ = ('d', 'e', 'f')
>>> SlottedDeriv().a = 1
>>> SlottedDeriv().d = 1
>>> SlottedDeriv().g = 1
Traceback (most recent call last):
File "<pyshell#79>", line 1, in <module>
SlottedDeriv().g = 1
AttributeError: 'SlottedDeriv' object has no attribute 'g'

The alternative would be for __slots__ to normally copy down, and to
have to be explicitly removed - for "pass" to be actually equivalent
to this:

>>> class Deriv(Base): __slots__ = Base.__slots__
>>> Deriv().d = 1
Traceback (most recent call last):
File "<pyshell#82>", line 1, in <module>
Deriv().d = 1
AttributeError: 'Deriv' object has no attribute 'd'

and for some other syntax to do what "pass" currently does. That has
the benefit of purity (it's easy to describe what happens, there's no
magic going on), but at the expense of practicality (you'd have to
explicitly de-slottify your classes before you can add functionality
to them). And we know what the Zen of Python says about which of those
takes priority.

ChrisA

Roy Smith

unread,
Aug 3, 2014, 11:09:47 AM8/3/14
to
In article <87wqapl...@elektro.pacujo.net>,
Marko Rauhamaa <ma...@pacujo.net> wrote:

> I've reached a point where I think classes are a superfluous OO concept.
> You only need objects.

comp.lang.javascript is over that way -->

Marko Rauhamaa

unread,
Aug 3, 2014, 11:52:36 AM8/3/14
to
Roy Smith <r...@panix.com>:

> Marko Rauhamaa <ma...@pacujo.net> wrote:
>
>> I've reached a point where I think classes are a superfluous OO
>> concept. You only need objects.
>
> comp.lang.javascript is over that way -->

Thanks for the insight. I'm currently more absorbed by comp.lang.scheme,
though.

Now, Python is ducktyped. It is (rightly) considered bad taste to
consult the datatype (class) of an object. Compare these two
definitions:

class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def x(self):
return self.x

def y(self):
return self.y

and:

class Object: pass

def Point(x, y):
self = Object()
self.__dict__ = dict(x=lambda: x, y=lambda: y)
return self

For all practical purposes, the two definitions are identical even
though the latter doesn't specify any class. Inheritance needs to be
addressed, but that can be taken care of without classes as well.

Obviously, Python's syntax makes it convenient to deal with classes, and
there's no practical downside to it. However, conceptually, classes are
unnecessary baggage and at odds with ducktyping.


Marko

Mark Lawrence

unread,
Aug 3, 2014, 1:38:04 PM8/3/14
to pytho...@python.org
On 02/08/2014 20:58, Ben Finney wrote:
> Steven D'Aprano <steve+comp....@pearwood.info> writes:
>
>> If you need instances which carry state, then object is the wrong
>> class.
>
> Right. The ‘types’ module provides a SimpleNamespace class for the
> common “bag of attributes” use case::
>
> >>> import types
> >>> foo = types.SimpleNamespace()
> >>> foo.x = 3
> >>> foo
> namespace(x=3)
>
> <URL:https://docs.python.org/3/library/types.html#types.SimpleNamespace>
>

A slight aside but from the link "SimpleNamespace may be useful as a
replacement for class NS: pass." I'm not quite sure how that class
definition is meant to read, other than guessing that NS stands for
NameSpace, any ideas?

Roy Smith

unread,
Aug 3, 2014, 1:44:14 PM8/3/14
to
In article <mailman.12592.1407087...@python.org>,
Mark Lawrence <bream...@yahoo.co.uk> wrote:

> On 02/08/2014 20:58, Ben Finney wrote:
> > Steven D'Aprano <steve+comp....@pearwood.info> writes:
> >
> >> If you need instances which carry state, then object is the wrong
> >> class.
> >
> > Right. The ‘types’ module provides a SimpleNamespace class for the
> > common “bag of attributes” use case::
> >
> > >>> import types
> > >>> foo = types.SimpleNamespace()
> > >>> foo.x = 3
> > >>> foo
> > namespace(x=3)
> >
> > <URL:https://docs.python.org/3/library/types.html#types.SimpleNamespace>
> >
>
> A slight aside but from the link "SimpleNamespace may be useful as a
> replacement for class NS: pass." I'm not quite sure how that class
> definition is meant to read, other than guessing that NS stands for
> NameSpace, any ideas?

Trivia: argparse.ArgumentParser().parse_args() returns a Namespace.

Terry Reedy

unread,
Aug 3, 2014, 6:55:13 PM8/3/14
to pytho...@python.org
On 8/3/2014 10:51 AM, Marko Rauhamaa wrote:
> Peter Otten <__pet...@web.de>:
>
>> i. e. you have a per-class and per-instance memory consumption. The
>> latter is smaller, so with regards to memory consumption instantiating
>> only pays off when there is more than one employee.
>
> I've reached a point where I think classes are a superfluous OO concept.
> You only need objects.
>
> Python is close to that reality. The "class" keyword really creates a
> function that creates objects, and the objects' class membership is
> ignored in ducktyping.

The object class is used to implement duck typing. It is the delegation
of operations to class instance methods that makes extensible duck
typing possible.

'a + b' is equivalent to type(a).__add__(a, b)

--
Terry Jan Reedy

Steven D'Aprano

unread,
Aug 3, 2014, 8:13:33 PM8/3/14
to
Marko Rauhamaa wrote:

> I've reached a point where I think classes are a superfluous OO concept.
> You only need objects.

I don't know whether "superfluous" is correct, but they certainly are
*optional*. There are at least two types of object oriented programming:
class-bases, and prototype-based. Java, Python, Ruby, etc. are all
class-based. The only example of a prototype-based OOP language I know of
is Javascript, and even that has recently gained the ability to define
classes.

I don't know enough about prototyped OOP to really give a definitive answer,
but I believe that the popularity of class-based OOP is because there is a
huge body of theory on types, which makes it easier for compiler designers
to reason about classes than prototypes. For example, if Haskell introduces
OOP (and it may already have, I don't know) I would expect it to be
class-based. Also, the first OOP languages (Simula and, especially,
Smalltalk) are class-based, and people tend to copy what's been done before
and what they're familiar with.


--
Steven

Roy Smith

unread,
Aug 3, 2014, 8:59:17 PM8/3/14
to
In article <53ded02e$0$29980$c3e8da3$5496...@news.astraweb.com>,
The first code I ever saw which had any OO concepts was the Unix kernel
I/O drivers. The whole thing was written in C. It essentially
implemented something we would recognize today as static class
inheritance with polymorphic method dispatch.

Each driver was required to implement a number of functions (read,
write, open, etc), with specified signatures and contracts.
Effectively, the all subclassed a (mythical) BaseIODriver. There was a
big table where the rows were the different drivers (indexed by major
device number), and the columns were the different methods. That was in
the early 70s.

Gregory Ewing

unread,
Aug 3, 2014, 9:00:57 PM8/3/14
to
Steven D'Aprano wrote:
> I don't know enough about prototyped OOP to really give a definitive answer,
> but I believe that the popularity of class-based OOP is because there is a
> huge body of theory on types,

I think it's more than that. I thought about prototype-based
OO systems in some depth a while ago, and I came to the
conclusion that the supposed simplification that comes
from not having classes is an illusion.

It *seems* simpler to have only one kind of object and
allow any object to inherit from any other. But actually
using it in such an undisciplined way would lead to chaos.

In practice, you end up with two groups of objects, with
one being used in a class-like way, and the other in an
instance-like way. So you effectively have classes anyway,
whether you call them that or not.

--
Greg

Steven D'Aprano

unread,
Aug 3, 2014, 9:19:29 PM8/3/14
to
Terry Reedy wrote:

> The object class is used to implement duck typing. It is the delegation
> of operations to class instance methods that makes extensible duck
> typing possible.

That cannot possibly be true, because Python had duck typing before it had
object. object and new-style classes were introduced in Python 2.2, which
means that there were a good half-dozen versions of Python, including the
very popular 1.5, where people couldn't use object.

(Also, I'm not sure what you mean by "class instance methods" -- methods can
be class methods, or they can be instance methods, but not both at the same
time.)

> 'a + b' is equivalent to type(a).__add__(a, b)

It's actually more complicated than that, because type(b).__radd__ also gets
considered, but either way, I don't think that the *implementation* of + is
relevant here.

Duck-typing isn't really a mechanism, in the sense that static/dynamic or
strong/weak typing are mechanisms:

- static typing means that the compiler can tell at compile-time what type
a variable will have, and prohibit code which may violate that constraint;

- dynamic typing means that types are associated with values, not with
variables;

- strong typing means that the compiler will do nothing (or very little)
to automatically convert values from one type to another;

- weak typing means that the compiler will do a lot to automatically
convert values from one type to another, including possibly some
conversions which are considered by many to be unsafe or silly.


Duck-typing is more of a programming philosophy than a mechanism, at least
in Python:

- you shouldn't care whether a value has a specific type or not, but
whether it exposes the interface you care about.

Some languages (like Java) try to formalise this, providing a mechanism by
which you can implement a particular interface in a way known to the
compiler:

https://en.wikipedia.org/wiki/Interface_%28Java%29

Python's ABCs (abstract base classes, introduced in version 2.6) are
similar. In both cases, they use the type system (in Java's case, at
compile-time, in Python's case, at run-time) to check for an interface
up-front, i.e. a form of "Look Before You Leap".

In Python, duck-typing can also be more ad hoc and informal: if you want to
know whether an object provides a certain interface, you typically just try
it and see if it breaks, hoping that it will raise an exception sooner
rather than later (i.e. "Easier to Ask for Forgiveness than Permission").
That's why I call it more of a philosophy than a mechanism.

Duck-typing, of course, is not infallible. Suppose you're expecting an
Artist, and call the artist.draw() method, but somebody gives you a
Gunfighter instead. There's also the problem of what to do when an object
provides only *part* of an interface: sometimes, by the time you have to
ask forgiveness, you've already made irreversible changes to something (a
file, a database, or launched the missiles).



--
Steven

Terry Reedy

unread,
Aug 4, 2014, 1:26:14 AM8/4/14
to pytho...@python.org
On 8/3/2014 9:19 PM, Steven D'Aprano wrote:

stuff based on an understandable misunderstanding of what I wrote.

> Terry Reedy wrote:
>
>> The object class is used to implement duck typing. It is the delegation
>> of operations to class instance methods that makes extensible duck
>> typing possible.

I left off "'s". The object's class ...

'class instance methods' = class attributes that are instance methods. I
was alluding to the fact that magic methods are looked up directly on
the class and not the instance.

Sorry for the confusion.

--
Terry Jan Reedy

Marko Rauhamaa

unread,
Aug 4, 2014, 1:41:07 AM8/4/14
to
Steven D'Aprano <steve+comp....@pearwood.info>:

> Marko Rauhamaa wrote:
>
>> I've reached a point where I think classes are a superfluous OO concept.
>> You only need objects.
>
> I don't know whether "superfluous" is correct, but they certainly are
> *optional*. There are at least two types of object oriented programming:
> class-bases, and prototype-based.

And I'm talking about a third kind: object-based. It is in active
(albeit limited) use in scheme: <URL: http://irreal.org/blog/?p=40>. I'm
currently using the principle in a project of mine.

In Java, you use anonymous classes for the same thing. In Python, you
can think of the principle as one-time classes. So instead of writing:

class A:
def __init__(self, x, y, z):
self.x = x
self.d = y * y + z * z

def f(self):
return self.x - self.d

you write:

def A(x, y, z):
d = y * y + z * z

class Anonymous:
def f(self):
return x - d

return Anonymous()

Now, if you always did this, you would notice that classes are
unnecessary clutter and would call for syntax like this:

def A(x, y, z):
d = y * y + z * z
return object:
def f():
return x - d


Marko

Ian Kelly

unread,
Aug 4, 2014, 2:41:32 AM8/4/14
to Python
On Sun, Aug 3, 2014 at 11:41 PM, Marko Rauhamaa <ma...@pacujo.net> wrote:
> Steven D'Aprano <steve+comp....@pearwood.info>:
> And I'm talking about a third kind: object-based. It is in active
> (albeit limited) use in scheme: <URL: http://irreal.org/blog/?p=40>. I'm
> currently using the principle in a project of mine.
>
> In Java, you use anonymous classes for the same thing. In Python, you
> can think of the principle as one-time classes. So instead of writing:

In my experience, 99% of the time when anonymous classes are used,
they only contain one method. That's because they're not really being
used to implement some interface abstractly; they mostly exist to
provide a means of specifying a callback function in a language where
functions aren't first-class.

>
> class A:
> def __init__(self, x, y, z):
> self.x = x
> self.d = y * y + z * z
>
> def f(self):
> return self.x - self.d
>
> you write:
>
> def A(x, y, z):
> d = y * y + z * z
>
> class Anonymous:
> def f(self):
> return x - d
>
> return Anonymous()

And it's the same thing here. This isn't an interface. It's a
function, so just return a function and be done with it. And in the
rare event that you actually mean to implement more than one method,
then you have enough functionality localized that it's probably
worthwhile to just name it and make it an actual class.

> Now, if you always did this, you would notice that classes are
> unnecessary clutter and would call for syntax like this:
>
> def A(x, y, z):
> d = y * y + z * z
> return object:
> def f():
> return x - d

The presence of "object" here feels totally unnecessary to me. If the
point is to avoid having a class, then why create a class? In this
case you could easily just return a function. To take a more complex
case, say the Scheme make-queue constructor that you linked to, you
could still just return a function; the same could be implemented in
Python 3 as:


def make_queue():
front = []
back = []
def queue_command(command, data=None):
def exchange():
nonlocal front, back
front = back; front.reverse()
back = []
if command == 'push':
back.append(data)
elif command == 'pop':
if not front: exchange()
return front.pop()
elif command == 'peek':
if not front: exchange()
return front[-1]
elif command == 'show':
return str(list(reversed(front)) + back)
else:
raise ValueError('Illegal command to queue object ' + command)
queue_command.push = lambda data: queue_command('push', data)
queue_command.pop = lambda: queue_command('pop')
queue_command.peek = lambda: queue_command('peek')
return queue_command


>>> queue = make_queue()
>>> queue.push(1)
>>> queue.push(2)
>>> queue.pop()
1
>>> queue.push(3)
>>> queue.push(4)
>>> queue.pop()
2
>>> queue.peek()
3
>>> queue.pop()
3
>>> queue.pop()
4

Marko Rauhamaa

unread,
Aug 4, 2014, 2:57:46 AM8/4/14
to
Ian Kelly <ian.g...@gmail.com>:

> In my experience, 99% of the time when anonymous classes are used,
> they only contain one method. [...]
>
>> def A(x, y, z):
>> d = y * y + z * z
>>
>> class Anonymous:
>> def f(self):
>> return x - d
>>
>> return Anonymous()
>
> And it's the same thing here. This isn't an interface. It's a
> function, so just return a function and be done with it.

My one-time classes usually have more than one method:

def query(self, domain_name, record_type, listener, xid = None):
...
client = self
class Operation:
def cancel(self):
if key in client.opmap and client.opmap[key] is self:
del client.opmap[key]
def callback():
client.log('CANCELED')
listener(client.CANCELED, None)
client.mux.schedule(callback)
def notify(self, verdict, records):
client.log('verdict = {}'.format(verdict))
listener(verdict, records)
operation = Operation()
self.opmap[key] = operation
...


Marko

Brian Blais

unread,
Aug 5, 2014, 7:36:17 AM8/5/14
to Mark Summerfield, pytho...@python.org
On Sat, Aug 2, 2014 at 2:45 AM, Mark Summerfield <li...@qtrac.plus.com> wrote:
> Last week I spent a couple of days teaching two children (10 and 13 -- too big an age gap!) how to do some turtle graphics with Python. Neither had programmed Python before -- one is a Minecraft ace and the other had done Scratch.

When I've taught children (and adults!) with little programming
experience, I usually have a single import for all the things I want
them to use, something like:

from my_defined_functions import *

at the beginning of every script, usually named for the class I'm teaching.


>
> Suggestion #1: Make IDLE start in the user's home directory.

I use the iPython Notebook now for these things.

>
> Suggestion #2: Make all the turtle examples begin "from turtle import *" so no leading turtle. is needed in the examples.
>

in my universal import script I have the turtle imports, usually with
both from turtle import * and import turtle, so I have a choice.



> Suggestion #3: Make object(key=value, ...) legal and equiv of types.SimpleNamespace(key=value, ...).

I also make a data structure, a simple wrapper around dict, which I
call Struct, defined as:

class Struct(dict):

def __getattr__(self,name):

try:
val=self[name]
except KeyError:
val=super(Struct,self).__getattribute__(name)

return val

def __setattr__(self,name,val):

self[name]=val


then I can do:

x=Struct(a=5,b=10)
x.c=50
x['this']='that' # or access like a dict


bb



-----------------

bbl...@gmail.com
http://web.bryant.edu/~bblais

Marko Rauhamaa

unread,
Aug 5, 2014, 8:04:27 AM8/5/14
to
Brian Blais <bbl...@gmail.com>:

> class Struct(dict):
>
> def __getattr__(self,name):
>
> try:
> val=self[name]
> except KeyError:
> val=super(Struct,self).__getattribute__(name)
>
> return val
>
> def __setattr__(self,name,val):
>
> self[name]=val

Cool. I wonder if that should be built into dict.

Why can't I have:

>>> d = {}
>>> d.x = 3
>>> d
{'x': 3}


Marko

Skip Montanaro

unread,
Aug 5, 2014, 8:31:05 AM8/5/14
to Marko Rauhamaa, Python
On Tue, Aug 5, 2014 at 7:04 AM, Marko Rauhamaa <ma...@pacujo.net> wrote:
>
> I wonder if that should be built into dict.


Short answer, no. I'm sure it's been proposed before. Attributes ≠
keys. When you see something.somethingelse anywhere else in Python,
"somethingelse" is an attribute reference. When you see
something[somethingelse], "somethingelse" is an index value or
key. Why destroy that symmetry in dictionaries?

JavaScript objects have that feature. I find it mildly confusing
because whenever I see it I have to pause to consider whether the name
I am looking at is an attribute or a key. This little JS code I just
typed at my console prompt was also mildly surprising:

> var x = {};
undefined
> x.valueOf(47)
Object {}
> x["valueOf"] = 12
12
> x.valueOf
12
> x.valueOf(47)
TypeError: number is not a function

Still, in my own JS code I tend to lapse into that usage, probably
because so much other code out in the wild uses dot notation for key
references. (Doesn't make it right, just makes me lazy.)

Skip

Steven D'Aprano

unread,
Aug 5, 2014, 8:43:12 AM8/5/14
to
Marko Rauhamaa wrote:

> Why can't I have:
>
> >>> d = {}
> >>> d.x = 3
> >>> d
> {'x': 3}

Because it's horrible and a bad idea.

d = {'this': 23, 'word': 42, 'frog': 2, 'copy': 15, 'lunch': 93}
e = d.copy()

Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: 'int' object is not callable


Conflating keys in a database with object attributes is one of the classic
blunders, like getting involved in a land war in Asia. It is, *maybe*,
barely acceptable as a quick-and-dirty convenience at the interactive
interpreter, in a Bunch or Bag class that has very little in the way of
methods or behaviour, but not acceptable for a class as fundamental and
important as dict. No matter what Javascript thinks.

Consider:

d['something else'] = 1
d.something else

d.something else
^
SyntaxError: invalid syntax



--
Steven

Chris Angelico

unread,
Aug 5, 2014, 9:00:56 AM8/5/14
to Python
On Tue, Aug 5, 2014 at 10:31 PM, Skip Montanaro <sk...@pobox.com> wrote:
> JavaScript objects have that feature. I find it mildly confusing
> because whenever I see it I have to pause to consider whether the name
> I am looking at is an attribute or a key. This little JS code I just
> typed at my console prompt was also mildly surprising:
>
>> var x = {};
> undefined
>> x.valueOf(47)
> Object {}
>> x["valueOf"] = 12
> 12
>> x.valueOf
> 12
>> x.valueOf(47)
> TypeError: number is not a function

This is partly a consequence of prototype-based inheritance; x.valueOf
will shadow Object.valueOf. Pike allows a similar "dot or square
brackets" notation, but at the expense of not having any methods on
the mapping type itself:

Pike v8.0 release 3 running Hilfe v3.5 (Incremental Pike Frontend)
> mapping a=([]);
> a.foo="bar";
(1) Result: "bar"
> a.foo;
(2) Result: "bar"
> a;
(3) Result: ([ /* 1 element */
"foo": "bar"
])

Since mappings (broadly equivalent to Python dicts) can't have
methods, anything that Python does as a method, Pike has to do as a
stand-alone function. (Swings and roundabouts, though, as those
functions tend to also accept other types, so they're more akin to
Python's len() than dict.pop().) Every design decision has a cost. The
convenience of short-handing mapping lookup is pretty handy
(especially when you're digging into deeply-nested mappings - imagine
parsing an XML or JSON message and then reaching into it for one
specific thing), but it means there are functions rather than methods
for working with them. Take your pick.

ChrisA

Chris Angelico

unread,
Aug 5, 2014, 9:08:34 AM8/5/14
to pytho...@python.org
On Tue, Aug 5, 2014 at 10:43 PM, Steven D'Aprano
<steve+comp....@pearwood.info> wrote:
> Because it's horrible and a bad idea.
>
> d = {'this': 23, 'word': 42, 'frog': 2, 'copy': 15, 'lunch': 93}
> e = d.copy()
>
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> TypeError: 'int' object is not callable
>
>
> Conflating keys in a database with object attributes is one of the classic
> blunders, like getting involved in a land war in Asia. It is, *maybe*,
> barely acceptable as a quick-and-dirty convenience at the interactive
> interpreter, in a Bunch or Bag class that has very little in the way of
> methods or behaviour, but not acceptable for a class as fundamental and
> important as dict. No matter what Javascript thinks.

It's not fundamentally bad, just fundamentally incompatible with any
other use of methods. Really, what you're pointing out isn't so much a
problem with conflating keys and attributes as it is with using
attributes for two purposes: key lookup, and method lookup. And that's
*always* going to be a bad thing. Imagine this class:

class BadDict(dict):
def __getitem__(self, key):
if key == 'copy': return self.copy
return super().__getitem__(key)

Now I've just gone the other way - overloading square brackets to
sometimes mean key lookup, and sometimes attribute lookup. And it's
the two meanings on one notation, not the two notations for one
meaning, that's the bad idea.

ChrisA

Marko Rauhamaa

unread,
Aug 5, 2014, 9:19:48 AM8/5/14
to
Skip Montanaro <sk...@pobox.com>:

> On Tue, Aug 5, 2014 at 7:04 AM, Marko Rauhamaa <ma...@pacujo.net> wrote:
>> I wonder if that should be built into dict.
>
> Short answer, no. I'm sure it's been proposed before. Attributes ≠
> keys. When you see something.somethingelse anywhere else in Python,
> "somethingelse" is an attribute reference. When you see
> something[somethingelse], "somethingelse" is an index value or key.
> Why destroy that symmetry in dictionaries?

I'm not sure I fully appreciate the dichotomy (which you euphemistically
refer to as symmetry).

> JavaScript objects have that feature. I find it mildly confusing
> because whenever I see it I have to pause to consider whether the name
> I am looking at is an attribute or a key.

What's the inherent difference between an attribute and a key.


Marko

MRAB

unread,
Aug 5, 2014, 10:11:29 AM8/5/14
to pytho...@python.org
An attribute is part of a container; a key is part of its contents.

Steven D'Aprano

unread,
Aug 5, 2014, 12:14:49 PM8/5/14
to
Marko Rauhamaa wrote:

> What's the inherent difference between an attribute and a key.


Here is my bucket. The handle of the bucket is part of the bucket:

bucket.handle

The pieces of coal I carry in the bucket is part of its content:

bucket['coal']


Of course, we can blur the distinction between part of the object and the
object's content, if we so choose. As Lewis Carroll might have written
in "Through the Looking Glass" had he been a programmer:

‘When I use syntax,’ Humpty Dumpty said in rather a scornful tone,
‘it means just what I choose it to mean — neither more nor less.’

’The question is,’ said Alice, ‘whether you can make syntax mean so
many different things.’

’The question is,’ said Humpty Dumpty, ‘which is to be master —
that’s all.’

If anyone wants a language where the same syntax means radically different
things, or radically different syntax means the same thing, then you know
where to find Perl, PHP and Javascript *wink*.

But more seriously, as Humpty might have said, of course the designer is the
master (and there are good masters and bad masters...), and sometimes there
is good reason to use attribute syntax for content. Sometimes it isn't
clear what counts as part of the object and what counts as part of the
contents, e.g. record or struct like objects exist in that grey area. In
that case, it may be a reasonable design choice to use attribute notation,
as namedtuple does, for example. But you'll note the cost: namedtuple is
forced to use leading underscore method names as *public* parts of the API,
so that they don't clash with field names.

If Guido was more like Perl's designer, Larry Wall, Python might have grown
a "short cut" notation for keys, say mydict$key, but the proliferation
of "many ways to do it" (to paraphrase the Perl motto) has costs of its
own. It's harder to learn, read and understand Perl code than Python code,
simply because there's more syntax to learn, and more special cases to
understand.


--
Steven

0 new messages