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

syntax for code blocks

205 views
Skip to first unread message

Kiuhnm

unread,
Apr 27, 2012, 7:24:35 AM4/27/12
to
I'd like to change the syntax of my module 'codeblocks' to make it more
pythonic.

Current Syntax:

with res << func(arg1) << 'x, y':
print(x, y)

with res << func(arg1) << block_name << 'x, y':
print(x, y)

New Syntax:

with res == func(arg1) .taking_block (x, y):
print(x, y)

with res == func(arg1) .taking_block (x, y) as block_name:
print(x, y)

The full form is equivalent to

def anon_func(x, y):
print(x, y)
res = func(arg1, block_name = anon_func)

Suggestions?

Kiuhnm

Steven D'Aprano

unread,
Apr 27, 2012, 10:09:30 AM4/27/12
to
On Fri, 27 Apr 2012 13:24:35 +0200, Kiuhnm wrote:

> I'd like to change the syntax of my module 'codeblocks' to make it more
> pythonic.
>
> Current Syntax:
>
> with res << func(arg1) << 'x, y':
> print(x, y)
>
> with res << func(arg1) << block_name << 'x, y':
> print(x, y)


I'm sorry, I don't see how this is a code block. Where is the code in the
block, and how can you pass it to another object to execute it?



--
Steven

Kiuhnm

unread,
Apr 27, 2012, 11:03:19 AM4/27/12
to
Maybe if you read the entire post...

Kiuhnm

Steven D'Aprano

unread,
Apr 27, 2012, 12:07:53 PM4/27/12
to
No, I read the entire post. It made no sense to me. Let me give one
example. You state:

The full form is equivalent to
def anon_func(x, y):
print(x, y)
res = func(arg1, block_name = anon_func)

but this doesn't mean anything to me. What's func? Where does it come
from? What's arg1? Why does something called block_NAME have a default
value of a function instead of a NAME?

How about you give an actual working example of what you mean by a code
block and how you use it?


--
Steven

Kiuhnm

unread,
Apr 28, 2012, 6:34:50 AM4/28/12
to
The rewriting rules are the following, where X ---> Y means that X is rewritten as Y on the fly:

1)
with res << func(args) << 'x, y':
<code>

--->

def anon_func(x, y):
<code>
res = func(args, anon_func)

2)
with res << func(args) << block_name << 'x, y':
<code>

--->

def anon_func(x, y):
<code>
res = func(args, block_name = anon_func)

That's all.
func is some function which takes a function as a positional argument or as a keyword parameter neamed block_name.

Some examples:

1)
text = "Anyone should be able to read this message!"
with ris << re.sub(r'(\w)(\w+)(\w)', string = text) << repl << 'm':
inner_word = list(m.group(2))
random.shuffle(inner_word)
_return (m.group(1) + "".join(inner_word) + m.group(3))
print(ris)

which prints (something like):

Aynnoe shluod be albe to read tihs msgseae!

2)
numbers = [random.randint(1, 100) for i in range(30)]

with sorted1 << sorted(numbers) << key << 'x':
if x <= 50:
_return(-x)
else:
_return(x)

print(sorted1)

which prints (something like):
[50, 47, 46, 28, 28, 25, 24, 23, 21, 19, 16, 15, 14, 3, 52, 52, 53, 54, 58, 62,
63, 69, 70, 72, 74, 78, 84, 86, 90, 97]

Kiuhnm

Peter Pearson

unread,
Apr 29, 2012, 12:05:47 PM4/29/12
to
On Fri, 27 Apr 2012 13:24:35 +0200, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote:
> I'd like to change the syntax of my module 'codeblocks' to make it more
> pythonic.
>
> Current Syntax:
>
> with res << func(arg1) << 'x, y':
> print(x, y)
>
> with res << func(arg1) << block_name << 'x, y':
> print(x, y)
>
> New Syntax:
>
> with res == func(arg1) .taking_block (x, y):
> print(x, y)
>
> with res == func(arg1) .taking_block (x, y) as block_name:
> print(x, y)
[snip]

Hey, guys, am I the only one here who can't even guess what
this code does? When did Python become so obscure?

--
To email me, substitute nowhere->spamcop, invalid->net.

Ben Finney

unread,
Apr 29, 2012, 11:30:56 PM4/29/12
to
Peter Pearson <ppea...@nowhere.invalid> writes:

> On Fri, 27 Apr 2012 13:24:35 +0200, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote:

> > I'd like to change the syntax of my module 'codeblocks' to make it
> > more pythonic.

The “chained callable” style isn't very Pythonic, IMO. Even worse is the
penchant for ‘foo .bar()’, the space obscures the fact that this is
attribute access.

Far from Pythonic, this seems to be an attempt to write some other
language in Python code.

> > Current Syntax:
> >
> > with res << func(arg1) << 'x, y':
> > print(x, y)
> >
> > with res << func(arg1) << block_name << 'x, y':
> > print(x, y)
> >
> > New Syntax:
> >
> > with res == func(arg1) .taking_block (x, y):
> > print(x, y)
> >
> > with res == func(arg1) .taking_block (x, y) as block_name:
> > print(x, y)
> [snip]
>
> Hey, guys, am I the only one here who can't even guess what
> this code does? When did Python become so obscure?

No, you're not alone; I think that code is pretty poor at communicating
the intent.

AFAICT the above code is a proposal. I don't know who (other than the
original poster) thinks it's any good.

--
\ “I cannot be angry at God, in whom I do not believe.” —Simone |
`\ De Beauvoir |
_o__) |
Ben Finney

mwi...@the-wire.com

unread,
Apr 30, 2012, 10:17:08 AM4/30/12
to
Ben Finney wrote:

> [ ... ] Even worse is the
> penchant for ‘foo .bar()’, the space obscures the fact that this is
> attribute access.

I like the style sometimes when it helps to break the significantly different parts out of
boilerplate:

libbnem. BN_add .argtypes = [ctypes.POINTER (BignumType), ctypes.POINTER (BignumType),
ctypes.POINTER (BignumType)]
libbnem. BN_add .restype = ctypes.c_int
libbnem. BN_add_word .argtypes = [ctypes.POINTER (BignumType), ctypes.c_ulong]
libbnem. BN_add_word .restype = ctypes.c_int

libbnem. BN_sub .argtypes = [ctypes.POINTER (BignumType), ctypes.POINTER (BignumType),
ctypes.POINTER (BignumType)]
libbnem. BN_sub .restype = ctypes.c_int
libbnem. BN_sub_word .argtypes = [ctypes.POINTER (BignumType), ctypes.c_ulong]
libbnem. BN_sub_word .restype = ctypes.c_int

(there were a lot more in the original program where those came from.) Another take-away
might be don't use boilerplate, but in the situation I didn't see a simple way to avoid it.

Mel.

Kiuhnm

unread,
Apr 30, 2012, 11:02:53 AM4/30/12
to
BignumTypePtr = ctypes.POINTER(BignumType)

for op, op_word in ((libbnem.BN_add, libbnem.BN_add_word),
(libbnem.BN_sub, libbnem.BN_sub_word)):
op.argtypes = [BignumTypePtr] * 3
op_word.argtypes = [BignumTypePtr, ctypes.c_ulong]
op.restype = op_word.restype = ctypes.c_int

Kiuhnm

Kiuhnm

unread,
Apr 30, 2012, 11:24:09 AM4/30/12
to
On second thought, BignumPtrType is probably the right name.

Kiuhnm

mwi...@the-wire.com

unread,
Apr 30, 2012, 11:42:04 AM4/30/12
to
(Way off the original topic, aren't we?) I haven't looked inside ctypes,
and don't know what kind of thing ctypes.POINTER actually constructs. I was
worried about falling into a [[a]]*3 kind of error -- unwittingly sharing a
mutable object. I guess I really should look.

Mel.

Kiuhnm

unread,
Apr 30, 2012, 11:54:40 AM4/30/12
to
Better off topic than uninterestingly in topic, IMHO.

Regarding ctypes, try this to convince yourself that there's no problem
in reusing BignumPtrType:
from ctypes import POINTER, c_int
assert POINTER(c_int) is POINTER(c_int)

Kiuhnm

mwi...@the-wire.com

unread,
Apr 30, 2012, 12:05:08 PM4/30/12
to
Kiuhnm wrote:
> Regarding ctypes, try this to convince yourself that there's no problem
> in reusing BignumPtrType:
> from ctypes import POINTER, c_int
> assert POINTER(c_int) is POINTER(c_int)

print ('POINTERs are shareable:', ctypes.POINTER (BignumType) is ctypes.POINTER
(BignumType))
[ ... ]
('POINTERs are shareable:', True)

Thanks.
Mel.

Ben Finney

unread,
Apr 30, 2012, 10:40:26 PM4/30/12
to
mwi...@the-wire.com writes:

> Another take-away might be don't use boilerplate, but in the situation
> I didn't see a simple way to avoid it.

It seems we agree, then, that avoiding boilerplate code is preferable to
writing bad boilerplate code.

--
\ “Computer perspective on Moore's Law: Human effort becomes |
`\ twice as expensive roughly every two years.” —anonymous |
_o__) |
Ben Finney

alex23

unread,
Apr 30, 2012, 11:27:44 PM4/30/12
to
On Apr 30, 2:05 am, Peter Pearson <ppear...@nowhere.invalid> wrote:
> Hey, guys, am I the only one here who can't even guess what
> this code does?  When did Python become so obscure?

Thankfully it hasn't. The most Pythonic way to pass around a code
block is still to use a function.

Kiuhnm

unread,
May 1, 2012, 10:18:03 AM5/1/12
to
"Most Pythonic" doesn't mean better, unfortunately.

For instance, assume that you want to write a function that accepts a
dictionary of callbacks:
func(some_args, callbacks)

Pythonic way
------------

def when_odd(n):
pass

def when_prime(n):
pass

def before_check():
pass

def after_check():
pass

func(some_args, {'when_odd' : when_odd,
'when_prime' : when_prime,
'before_check' : before_check,
'after_check' : after_check})

def when_prime(n):
pass

def when_perfect(n):
pass

def before_reduction()
pass

def after_reduction():
pass

func(some_args, {'when_prime' : when_prime,
'when_perfect' : when_perfect,
'before_reduction' : before_reduction,
'after_reduction' : after_reduction})

My way
------

with func(some_args) << ':dict':
with when_odd as 'n':
pass
with when_prime as 'n':
pass
with before_check as '':
pass
with after_check as '':
pass

with func(some_args) << ':dict':
with when_prime as 'n':
pass
with when_perfect as 'n':
pass
with before_reduction as '':
pass
with after_reduction as '':
pass

Kiuhnm

Steven D'Aprano

unread,
May 1, 2012, 11:11:03 AM5/1/12
to
On Tue, 01 May 2012 16:18:03 +0200, Kiuhnm wrote:

> "Most Pythonic" doesn't mean better, unfortunately.

Perhaps. But this example is not one of those cases.


> For instance, assume that you want to write a function that accepts a
> dictionary of callbacks:
> func(some_args, callbacks)
>
> Pythonic way
> ------------
>
> def when_odd(n):
> pass
[snip multiple function definitions]

> func(some_args, {'when_odd' : when_odd,
> 'when_prime' : when_prime,
> 'before_check' : before_check,
> 'after_check' : after_check})

> My way
> ------
>
> with func(some_args) << ':dict':
> with when_odd as 'n':
> pass
> with when_prime as 'n':
> pass


If you actually try that, you will see that it cannot work. You get:

SyntaxError: can't assign to literal


Have you actually tried to use these code blocks of yours? I asked you
for a *working* example earlier, and you replied with examples that
failed with multiple NameErrors and no hint as to how to fix them. And
now you give an example that fails with SyntaxError. I'm reluctantly
coming to the conclusion that these "code blocks" of yours simply do not
work.


> with before_check as '':
> pass
> with after_check as '':
> pass

You have a bug in one or more of those callbacks.

Of course you do -- all non-trivial software has bugs. The question is,
how are you going to find it? You can't unit-test the individual
callbacks, because they don't exist in a form that can be tested.

So in this case, even though Python is slightly more verbose, and forces
you to have the discipline of writing named functions ahead of time, this
is actually a *good* thing because it encourages you to test them.

If the callbacks are trivial functions, the Pythonic way is to use
lambdas:

func(some_args, {'when_odd': lambda n: n-1,
'when_prime': lambda n: n**2 - 1,
...})

although I would discourage that unless they are *really* trivial. But
for callbacks of any complexity, they will need to be tested, otherwise
how do you know they do what you want them to do?

Your code blocks make unit testing of the callbacks impossible, and for
that reason the Pythonic way is better.


--
Steven

Kiuhnm

unread,
May 1, 2012, 1:07:58 PM5/1/12
to
On 5/1/2012 17:11, Steven D'Aprano wrote:
>> My way
>> ------
>>
>> with func(some_args)<< ':dict':
>> with when_odd as 'n':
>> pass
>> with when_prime as 'n':
>> pass
>
>
> If you actually try that, you will see that it cannot work. You get:
>
> SyntaxError: can't assign to literal

If you had read the module's docstring you would know that the public
version uses
with when_odd << 'n':
pass

> Have you actually tried to use these code blocks of yours? I asked you
> for a *working* example earlier, and you replied with examples that
> failed with multiple NameErrors and no hint as to how to fix them. And
> now you give an example that fails with SyntaxError.

The examples I gave you work perfectly. It's clear that you don't even
have the vaguest idea of how my module works or, otherwise, you'd know
what you're doing wrong. Again, the module's docstring is your friend.

> You have a bug in one or more of those callbacks.
>
> Of course you do -- all non-trivial software has bugs. The question is,
> how are you going to find it? You can't unit-test the individual
> callbacks, because they don't exist in a form that can be tested.

It's easy to come up with a solution, in fact those functions /do/
exist. You would know that if you had read the documentation or even my
reply to a post of yours.

> So in this case, even though Python is slightly more verbose, and forces
> you to have the discipline of writing named functions ahead of time, this
> is actually a *good* thing because it encourages you to test them.
>
> If the callbacks are trivial functions, the Pythonic way is to use
> lambdas:
>
> func(some_args, {'when_odd': lambda n: n-1,
> 'when_prime': lambda n: n**2 - 1,
> ...})
>
> although I would discourage that unless they are *really* trivial. But
> for callbacks of any complexity, they will need to be tested, otherwise
> how do you know they do what you want them to do?
>
> Your code blocks make unit testing of the callbacks impossible, and for
> that reason the Pythonic way is better.

Talking with you is a real pain. You're always partial in your opinions
and this urge of yours to criticize other's work makes you look dumb or
hopefully just lazy.
I can't stand people like you who don't even have the decency of taking
the time to read the documentation of a project and just run their mouth
without any concern for facts.
What I can't stand is that if I won't reply to your posts other lazy
people will believe the nonsense you say, but I'll have to live with
that because I've wasted enough time with you.

Kiuhnm

Steven D'Aprano

unread,
May 1, 2012, 8:09:07 PM5/1/12
to
On Tue, 01 May 2012 19:07:58 +0200, Kiuhnm wrote:

> On 5/1/2012 17:11, Steven D'Aprano wrote:
>>> My way
>>> ------
>>>
>>> with func(some_args)<< ':dict':
>>> with when_odd as 'n':
>>> pass
>>> with when_prime as 'n':
>>> pass
>>
>>
>> If you actually try that, you will see that it cannot work. You get:
>>
>> SyntaxError: can't assign to literal
>
> If you had read the module's docstring

What module?


> you would know that the public version uses
> with when_odd << 'n':
> pass

Then why didn't you write that instead of something that gives
SyntaxError? Not once, but EIGHT times.


>> Have you actually tried to use these code blocks of yours? I asked you
>> for a *working* example earlier, and you replied with examples that
>> failed with multiple NameErrors and no hint as to how to fix them. And
>> now you give an example that fails with SyntaxError.
>
> The examples I gave you work perfectly.

Except they don't.

Look, this isn't difficult. I asked for *working* examples, you gave
examples that give NameError. Some of those errors are easy to fix, e.g.
by importing the random and re modules. Some of them aren't. What are
"ris", "repl", "_return", "sorted1", "key"? Where do they come from? What
value should I give them to make your code work?

Code doesn't magically start to work because you declare that it does.
Anyone who takes the time to copy and paste your examples into a fresh
Python interactive session will see that they don't.

Here's one of your earlier examples. I've removed the unnecessary
indentation and made the obvious import, so all it takes to run it is to
copy it and paste into the Python prompt:

import random
numbers = [random.randint(1, 100) for i in range(30)]
with sorted1 << sorted(numbers) << key << 'x':
if x <= 50:
_return(-x)
else:
_return(x)

print(sorted1)


If you try it, you get

NameError: name 'sorted1' is not defined

Fix that (how?) and you'll get another NameError, for 'key', and then a
third, for '_return'.

The syntax isn't very clear. If I had to guess what it does, I'd predict
that maybe it sorts the random list and negates everything <= 50, e.g.
given input [5, 99, 34, 88, 70, 2] it returns [-2, -5, -34, 70, 88, 99].
Obviously that's wrong. You say it should return [34, 5, 2, 70, 88, 99].

The Pythonic way to get that result is:

import random
numbers = [random.randint(1, 100) for i in range(30)]
sorted(numbers, key=lambda x: -x if x <= 50 else x)

which is much more straightforward and obvious than yours, and presumably
much faster. So even if I could get your example working, it would not be
very persuasive.


> It's clear that you don't even
> have the vaguest idea of how my module works or, otherwise, you'd know
> what you're doing wrong.

Well duh. What module? Where do I find it? How am I supposed to know what
this module is when you haven't mentioned it?

Believe it or not, the world does not revolve around you. We cannot see
what is in your head. If we ask for a WORKING EXAMPLE, you need to give
an example that includes EVERYTHING necessary to make it work.

[...]
> Talking with you is a real pain. You're always partial in your opinions
> and this urge of yours to criticize other's work makes you look dumb or
> hopefully just lazy.
> I can't stand people like you who don't even have the decency of taking
> the time to read the documentation of a project and just run their mouth
> without any concern for facts.

What project? You're the one who doesn't tell us what project we're
supposed to use, and yet *I'm* the dumb one.

This is comedy gold.


> What I can't stand is that if I won't reply to your posts other lazy
> people will believe the nonsense you say, but I'll have to live with
> that because I've wasted enough time with you.

Whatever man. It's no skin off my nose. I've tried really hard to give
your proposal a fair go, but my care factor is rapidly running out if you
can't even be bothered to ensure your examples use legal Python syntax.



--
Steven

Ben Finney

unread,
May 1, 2012, 10:31:55 PM5/1/12
to
Steven D'Aprano <steve+comp....@pearwood.info> writes:

> On Tue, 01 May 2012 19:07:58 +0200, Kiuhnm wrote:
> > [entitled demands]

> Believe it or not, the world does not revolve around you. We cannot
> see what is in your head. If we ask for a WORKING EXAMPLE, you need to
> give an example that includes EVERYTHING necessary to make it work.

To forestall the next obvious but wrong response: no, this is not asking
to see a massive complicated module dumped on us to pore over.

The term missing from Steven's request is “minimal”. When presenting
code for examination by others, we request that you present a complete,
minimal, working example.

Complete, so that we can take what you present and use it without
guessing (likely incorrect guesses) what extra bits you did.

Minimal, so that it contains *only* what is needed to demonstrate what
you're trying to communicate. Yes, this probably means writing an
example specifically to present to us; tough, the job falls to you if
you want us to spend time on your issue.

Working example: so that it actually does what you say it does, and we
can verify that directly instead of speculating.

> [...]
> > Talking with you is a real pain. You're always partial in your
> > opinions and this urge of yours to criticize other's work makes you
> > look dumb or hopefully just lazy.

Sometimes one side of a disagreement is just incorrect, and in those
cases it's good to be partial in one's opinions and to criticise on the
facts.

You have entered may people's kill files, including mine, because of
this inability to take criticism and this tendency to insult others
baselessly.

--
\ “Holy unrefillable prescriptions, Batman!” —Robin |
`\ |
_o__) |
Ben Finney

alex23

unread,
May 1, 2012, 10:43:57 PM5/1/12
to
[Apologies in advance if this comes through twice]

On May 2, 12:18 am, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote:
> "Most Pythonic" doesn't mean better, unfortunately.

Nor does it mean "Kiuhnm prefers it".

> For instance, assume that you want to write a function that accepts a
> dictionary of callbacks:
>    func(some_args, callbacks)
>
> Pythonic way
> ------------
>
> def when_odd(n):
>      pass
>
> def when_prime(n):
>      pass
>
> def before_check():
>      pass
>
> def after_check():
>      pass
>
> func(some_args, {'when_odd' : when_odd,
>                   'when_prime' : when_prime,
>                   'before_check' : before_check,
>                   'after_check' : after_check})

When would you _ever_ define a function like this? Why are you passing
in _named_ callbacks? Are you calling them by name? Then declare them
in the function signature and rely on scoping. Does func() branch on
existent keys? Then don't write code like that.

At the very _least_, you could change the function call to:

func(some_args, locals())

But as you're _passing them in by name_ why not just make it
func(some_args) and pick them up out of the scope.

_No one_ writes Python code like this. Presenting bad code as
"pythonic" is a bit of a straw man.

> My way
> ------
>
> with func(some_args) << ':dict':
>      with when_odd as 'n':
>          pass
>      with when_prime as 'n':
>          pass
>      with before_check as '':
>          pass
>      with after_check as '':
>          pass

This is unintuitive, to say the least. You're effectively replacing
the common form of function definition with "with when_odd as 'n'",
then using the surrounding context manager to limit the scope.

More importantly, _you're naming your "anonymous" code blocks_, I'm
guessing so that func() can choose which ones to use. But if you're
naming them, why not just use a function?

I'm not entirely sure what your 'solution' offers over something like:

class FOO(object):
def __init__(self, fn):
self.fn = fn

def __enter__(self):
return self.fn

def __exit__(self, exc_type, exc_value, traceback):
pass

def func(x, before_check=None, after_check=None, when_odd=None,
when_prime=None):
pass

with FOO(func) as f:
def before_check(x):
pass
def after_check(x):
pass
def when_odd(x):
pass
def when_prime(x):
pass

f(1)

I called the context manager FOO because I couldn't think of a more
appropriate name (contextscope?)...which is fair, as I can't think of
a use for it either. If you want to automate the function call at the
context manager exit, use something like this instead:

class FOO(object):
def __init__(self, fn, *args, **kwargs):
self.fn = fn
self.args = args
self.kwargs = kwargs

def __enter__(self):
f = inspect.currentframe(1)
self.scope_before = dict(f.f_locals)

def __exit__(self, exc_type, exc_value, traceback):
f = inspect.currentframe(1)
scope_after = dict(f.f_locals)
scope_context = set(scope_after) - set(self.scope_before)
clocals = dict(
[(k, scope_after[k]) for k in scope_context]
)
self.kwargs.update(clocals)
self.fn(*self.args, **self.kwargs)

Maybe there is a compelling use case for code blocks but your toy
example certainly isn't it.

alex23

unread,
May 1, 2012, 10:50:46 PM5/1/12
to
On May 2, 12:43 pm, alex23 <wuwe...@gmail.com> wrote:
> I'm not entirely sure what your 'solution' offers over something like:
>
> class FOO(object):
>     def __init__(self, fn):
>         self.fn = fn
>
>     def __enter__(self):
>         return self.fn
>
>     def __exit__(self, exc_type, exc_value, traceback):
>         pass
>
> def func(x, before_check=None, after_check=None, when_odd=None,
> when_prime=None):
>     pass

Sorry, the func definition was meant to be:

def func(x):
# before_check, after_check, when_odd, when_prime used
internally
pass

alex23

unread,
May 1, 2012, 10:22:23 PM5/1/12
to
On May 2, 12:18 am, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote:
> "Most Pythonic" doesn't mean better, unfortunately.

Neither does "Kiuhnm prefers it".

> For instance, assume that you want to write a function that accepts a
> dictionary of callbacks:
>    func(some_args, callbacks)
>
> Pythonic way
> ------------
>
> def when_odd(n):
>      pass
>
> def when_prime(n):
>      pass
>
> def before_check():
>      pass
>
> def after_check():
>      pass
>
> func(some_args, {'when_odd' : when_odd,
>                   'when_prime' : when_prime,
>                   'before_check' : before_check,
>                   'after_check' : after_check})

I'm sorry, when would you _ever_ do this? Why are you naming the
functions twice? If you're passing in a dynamic set of functions,
you'd _never know the names_, so my guess is you'd be iterating across
it. If you _do_ know the names, why aren't you accessing them directly
from the surrounding scope? Why aren't you including them in the
function signature?

Presenting bad examples as the Pythonic approach is a bit of a straw
man.

> My way
> ------
>
> with func(some_args) << ':dict':
>      with when_odd as 'n':
>          pass
>      with when_prime as 'n':
>          pass
>      with before_check as '':
>          pass
>      with after_check as '':
>          pass

I'm not sure what value your code blocks really provide.

1. You're effectively making "with when_odd as 'n'" mean "def
when_odd(n)"
2. The 'with code_block_name as arguments' syntax is unintuitive, to
say the least.

Personally, I don't see what value it provides over something more
explicit and standard:

def func(x, before_check=None, after_check=None, when_odd=None,
when_prime=None):
pass

with FOO(func, arg):
def before_check(x):
pass
def after_check(x):
pass
def when_odd(x):
pass
def when_prime(x):
pass

I couldn't think of a name for the context manager...but I can't
really think of a use for it either, so that seems fair.

Actually, here's an even simpler example. In this case, the context
manager doesn't have to interrogate the surrounding stack, but the
function call itself is then explicit.

class FOO(object):
def __init__(self, fn):
self.fn = fn

def __enter__(self):
return self.fn

def __exit__(self, exc_type, exc_value, traceback):
pass

def func(x, before_check=None, after_check=None, when_odd=None,
when_prime=None):
pass

with FOO(func) as f:
def before_check(x):
pass
def after_check(x):
pass
def when_odd(x):
pass
def when_prime(x):
pass

f(1)

But again _what do you gain from this_ other than an extra layer of
unnecessary complexity.

Kiuhnm

unread,
May 2, 2012, 6:52:50 AM5/2/12
to
On 5/2/2012 4:43, alex23 wrote:
> [Apologies in advance if this comes through twice]
>
> On May 2, 12:18 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote:
>> "Most Pythonic" doesn't mean better, unfortunately.
>
> Nor does it mean "Kiuhnm prefers it".

That goes without saying.

>> For instance, assume that you want to write a function that accepts a
>> dictionary of callbacks:
>> func(some_args, callbacks)
>>
>> Pythonic way
>> ------------
>>
>> def when_odd(n):
>> pass
>>
>> def when_prime(n):
>> pass
>>
>> def before_check():
>> pass
>>
>> def after_check():
>> pass
>>
>> func(some_args, {'when_odd' : when_odd,
>> 'when_prime' : when_prime,
>> 'before_check' : before_check,
>> 'after_check' : after_check})
>
> When would you _ever_ define a function like this? Why are you passing
> in _named_ callbacks? Are you calling them by name? Then declare them
> in the function signature and rely on scoping. Does func() branch on
> existent keys? Then don't write code like that.
>
> At the very _least_, you could change the function call to:
>
> func(some_args, locals())

I think that's very bad. It wouldn't be safe either. What about name
clashing and how would you pass only some selected functions?
A second call would also need some cleaning up leading to some serious bugs.

> But as you're _passing them in by name_ why not just make it
> func(some_args) and pick them up out of the scope.

Because that's not clean and maintainable. It's not different from using
global variables.

> _No one_ writes Python code like this. Presenting bad code as
> "pythonic" is a bit of a straw man.

How can I present good code where there's no good way of doing that
without my module or something equivalent?
That was my point.

>> My way
>> ------
>>
>> with func(some_args)<< ':dict':
>> with when_odd as 'n':
>> pass
>> with when_prime as 'n':
>> pass
>> with before_check as '':
>> pass
>> with after_check as '':
>> pass
>
> This is unintuitive, to say the least. You're effectively replacing
> the common form of function definition with "with when_odd as 'n'",
> then using the surrounding context manager to limit the scope.

What's so unintuitive about it? It's just "different".

> More importantly, _you're naming your "anonymous" code blocks_, I'm
> guessing so that func() can choose which ones to use. But if you're
> naming them, why not just use a function?

I'm creating a dictionary, not naming my blocks just for the sake of it.
If you use a function, you end up with the solution that you called
'bad' and non-pythonic.

> I'm not entirely sure what your 'solution' offers over something like:
>
> class FOO(object):
> def __init__(self, fn):
> self.fn = fn
>
> def __enter__(self):
> return self.fn
>
> def __exit__(self, exc_type, exc_value, traceback):
> pass
>
> def func(x, before_check=None, after_check=None, when_odd=None,
> when_prime=None):
> pass
>
> with FOO(func) as f:
> def before_check(x):
> pass
> def after_check(x):
> pass
> def when_odd(x):
> pass
> def when_prime(x):
> pass
>
> f(1)

The problem is always the same. Those functions are defined at the
module level so name clashing and many other problems are possible.

I remember a post on this ng when one would create a list of commands
and then use that list as a switch table. My module let you do that very
easily. The syntax is:

with func() << ':list':
with 'arg':
cmd_code
with 'arg':
cmd_code
with '':
cmd_code

Kiuhnm

alex23

unread,
May 2, 2012, 8:20:16 PM5/2/12
to
On May 2, 8:52 pm, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote:
>> func(some_args, locals())
>
> I think that's very bad. It wouldn't be safe either. What about name
> clashing

locals() is a dict. It's not injecting anything into func's scope
other than a dict so there's not going to be any name clashes. If you
don't want any of its content in your function's scope, just don't use
that content.

> and how would you pass only some selected functions?

You wouldn't. You would just refer to the required functions in the
dict _in the same way you would in both your "bad python" and code
block versions.

> > But as you're _passing them in by name_ why not just make it
> > func(some_args) and pick them up out of the scope.
>
> Because that's not clean and maintainable. It's not different from using
> global variables.

...I'm beginning to suspect we're not engaging in the same
conversation.

This is very common in Python:

from module1 import func1

def func2(args): pass

def main():
# do something with func1 and func2

And I've never had problems maintaining code like this. I know
_exactly_ the scope that the functions exist within because I added
them to it. They're not _global_ because they're restricted to that
specific scope.

> > _No one_ writes Python code like this. Presenting bad code as
> > "pythonic" is a bit of a straw man.
>
> How can I present good code where there's no good way of doing that
> without my module or something equivalent?
> That was my point.

You haven't presented *any* good code or use cases.

> > This is unintuitive, to say the least. You're effectively replacing
> > the common form of function definition with "with when_odd as 'n'",
> > then using the surrounding context manager to limit the scope.
>
> What's so unintuitive about it? It's just "different".

Because under no circumstance does "with function_name as
string_signature" _read_ in an understandable way. It's tortuous
grammar that makes no sense as a context manager. You're asking people
to be constantly aware that there are two completely separate meanings
to 'with x as y'.

> > More importantly, _you're naming your "anonymous" code blocks_, I'm
> > guessing so that func() can choose which ones to use. But if you're
> > naming them, why not just use a function?
>
> I'm creating a dictionary, not naming my blocks just for the sake of it.
> If you use a function, you end up with the solution that you called
> 'bad' and non-pythonic.

What I considered 'bad' was having a _single_ function that takes
_multiple differing collections_ of named functions. Now you've moved
the onus onto the caller to ensure that the function is provided what
it needs in a specific context to do its thing. Rather than overload
one single function and push the complexity out to the caller, why not
have multiple functions with obvious names about what they do that
only take the data they need to act on?

Then again, it's _really difficult_ to tell if something named
'func()' could have a real use like this.

> The problem is always the same. Those functions are defined at the
> module level so name clashing and many other problems are possible.

So define & use a different scope! Thankfully module level isn't the
only one to play with.

> I remember a post on this ng when one would create a list of commands
> and then use that list as a switch table. My module let you do that very
> easily. The syntax is:
>
>      with func() << ':list':
>          with 'arg':
>              cmd_code
>          with 'arg':
>              cmd_code
>          with '':
>              cmd_code

I'm sorry but it is still clear-as-mud what you're trying to show
here. Can you show _one_ practical, real-world, non-toy example that
solves a real problem in a way that Python cannot?

Kiuhnm

unread,
May 3, 2012, 12:17:05 PM5/3/12
to
On 5/3/2012 2:20, alex23 wrote:
> On May 2, 8:52 pm, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote:
>>> func(some_args, locals())
>>
>> I think that's very bad. It wouldn't be safe either. What about name
>> clashing
>
> locals() is a dict. It's not injecting anything into func's scope
> other than a dict so there's not going to be any name clashes. If you
> don't want any of its content in your function's scope, just don't use
> that content.

The clashing is *inside* the dictionary itself. It contains *all* local
functions and variables.

>> and how would you pass only some selected functions?
>
> You wouldn't. You would just refer to the required functions in the
> dict _in the same way you would in both your "bad python" and code
> block versions.

See above.

>>> But as you're _passing them in by name_ why not just make it
>>> func(some_args) and pick them up out of the scope.
>>
>> Because that's not clean and maintainable. It's not different from using
>> global variables.
>
> ...I'm beginning to suspect we're not engaging in the same
> conversation.
>
> This is very common in Python:
>
> from module1 import func1
>
> def func2(args): pass
>
> def main():
> # do something with func1 and func2
>
> And I've never had problems maintaining code like this. I know
> _exactly_ the scope that the functions exist within because I added
> them to it. They're not _global_ because they're restricted to that
> specific scope.

That's not the same thing. If a function accepts some optional
callbacks, and you call that function more than once, you will have
problems. You'll need to redefine some callbacks and remove others.
That's total lack of encapsulation.

>>> _No one_ writes Python code like this. Presenting bad code as
>>> "pythonic" is a bit of a straw man.
>>
>> How can I present good code where there's no good way of doing that
>> without my module or something equivalent?
>> That was my point.
>
> You haven't presented *any* good code or use cases.

Says who? You and some others? Not enough.

>>> This is unintuitive, to say the least. You're effectively replacing
>>> the common form of function definition with "with when_odd as 'n'",
>>> then using the surrounding context manager to limit the scope.
>>
>> What's so unintuitive about it? It's just "different".
>
> Because under no circumstance does "with function_name as
> string_signature" _read_ in an understandable way. It's tortuous
> grammar that makes no sense as a context manager. You're asking people
> to be constantly aware that there are two completely separate meanings
> to 'with x as y'.

The meaning is clear from the context. I would've come up with something
even better if only Python wasn't so rigid.

> Rather than overload
> one single function and push the complexity out to the caller, why not
> have multiple functions with obvious names about what they do that
> only take the data they need to act on?

Because that would reveal part of the implementation.
Suppose you have a complex visitor. The OOP way is to subclass, while
the FP way is to accept callbacks. Why the FP way? Because it's more
concise.
In any case, you don't want to reveal how the visitor walks the data
structure or, better, the user doesn't need to know about it.

> Then again, it's _really difficult_ to tell if something named
> 'func()' could have a real use like this.
>
>> The problem is always the same. Those functions are defined at the
>> module level so name clashing and many other problems are possible.
>
> So define& use a different scope! Thankfully module level isn't the
> only one to play with.

We can do OOP even in ASM, you know?

>> I remember a post on this ng when one would create a list of commands
>> and then use that list as a switch table. My module let you do that very
>> easily. The syntax is:
>>
>> with func()<< ':list':
>> with 'arg':
>> cmd_code
>> with 'arg':
>> cmd_code
>> with '':
>> cmd_code
>
> I'm sorry but it is still clear-as-mud what you're trying to show
> here. Can you show _one_ practical, real-world, non-toy example that
> solves a real problem in a way that Python cannot?

I just did. It's just that you can't see it.

Kiuhnm

alex23

unread,
May 3, 2012, 10:44:57 PM5/3/12
to
On May 4, 2:17 am, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote:
> On 5/3/2012 2:20, alex23 wrote:
> > locals() is a dict. It's not injecting anything into func's scope
> > other than a dict so there's not going to be any name clashes. If you
> > don't want any of its content in your function's scope, just don't use
> > that content.
>
> The clashing is *inside* the dictionary itself. It contains *all* local
> functions and variables.

This is nonsense.

locals() produces a dict of the local scope. I'm passing it into a
function. Nothing in the local scope clashes, so the locals() dict has
no "internal clashing". Nothing is injecting it into the function's
local scope, so _there is no "internal clashing"_.

To revise, your original "pythonic" example was, effectively:

def a(): pass
def b(): pass

func_packet = {'a': a, 'b': b}
func(arg, func_packet)

My version was:

def a(): pass
def b(): pass

func_packet = locals()
func(arg, func_packet)

Now, please explain how that produces name-clashes that your version
does not.

> >> and how would you pass only some selected functions?
>
> > You wouldn't. You would just refer to the required functions in the
> > dict _in the same way you would in both your "bad python" and code
> > block versions.
>
> See above.

This is more nonsense. So calling 'a' in your dict is fine, but
calling a in the locals() returned dict isn't?


> That's not the same thing. If a function accepts some optional
> callbacks, and you call that function more than once, you will have
> problems. You'll need to redefine some callbacks and remove others.
> That's total lack of encapsulation.

Hand-wavy, no real example, doesn't make sense.

> > You haven't presented *any* good code or use cases.
>
> Says who? You and some others? Not enough.

So far, pretty much everyone who has tried to engage you on this
subject on the list. I'm sorry we're not all ZOMGRUBYBLOCKS!!!!111
like the commenters on your project page.

> The meaning is clear from the context.

Which is why pretty much every post in this thread mentioned finding
it confusing?

> I would've come up with something even better if only Python wasn't so rigid.

The inability for people to add 6 billion mini-DSLs to solve any
stupid problem _is a good thing_. It makes Python consistent and
predictable, and means I don't need to parse _the same syntax_ utterly
different ways depending on the context.

> Because that would reveal part of the implementation.
> Suppose you have a complex visitor. The OOP way is to subclass, while
> the FP way is to accept callbacks. Why the FP way? Because it's more
> concise.
> In any case, you don't want to reveal how the visitor walks the data
> structure or, better, the user doesn't need to know about it.

Again, nothing concrete, just vague intimations of your way being
better.

> > So define&  use a different scope! Thankfully module level isn't the
> > only one to play with.
>
> We can do OOP even in ASM, you know?

???

> > I'm sorry but it is still clear-as-mud what you're trying to show
> > here. Can you show _one_ practical, real-world, non-toy example that
> > solves a real problem in a way that Python cannot?
>
> I just did. It's just that you can't see it.

"I don't understand this example, can you provide one." "I just did,
you didn't understand it."

Okay, done with this now. Your tautologies and arrogance are not
clarifying your position at all, and I really don't give a damn, so
*plonk*

Steven D'Aprano

unread,
May 3, 2012, 11:47:36 PM5/3/12
to
On Thu, 03 May 2012 19:44:57 -0700, alex23 wrote:

[snip]
> My version was:
>
> def a(): pass
> def b(): pass
>
> func_packet = locals()
> func(arg, func_packet)
>
> Now, please explain how that produces name-clashes that your version
> does not.

I too am uncomfortable about passing locals() to a function, but not
because of imaginary "name clashes". The problem as I see it is that this
will give the function access to things the function has no need for.

While CPython doesn't allow the called function to rebind names in the
local scope (except in the case where the local scope is also the global
scope), that may not apply to all Python implementations. So code which
works safely in CPython may break badly in some other implementation.

Another problem is that even in implementations where you can't rebind
locals, the called function might mutate them instead. If any of the
content of locals() are mutable, you're giving the function the potential
to mutate them, whether it needs that power or not.

Let me put it this way... suppose you had a function with a signature
like this:

def spam(a, b, c, **kwargs):
...


and you knew that spam() ignores keyword arguments that it doesn't need.
Or at least is supposed to. Suppose you needed to make this call:

spam(23, 42, ham=None, cheese="something")


Would you do this instead?

foo = ['some', 'list', 'of', 'things']
spam(23, 42, ham=None, cheese="something", aardvark=foo)

on the basis that since aardvark will be ignored, it is perfectly safe to
do so?

No, of course not, that would be stupid. Perhaps spam() has a bug that
will mutate the list even though it shouldn't touch it. More importantly,
you cause difficulty to the reader, who wonders why you are passing this
unused and unnecessary aardvark argument to the function.

My argument is that this is equivalent to passing locals() as argument.
Your local scope contains some arbitrary number of name bindings. Only
some of them are actually used. Why pass all (say) 25 of them if the
function only needs access to (say) three? To me, passing locals() as an
argument in this fashion is a code-smell: not necessary wrong or bad, but
a hint that something unusual and slightly worrying is going on, and you
should take a close look at it because there *may* be a problem.


> So far, pretty much everyone who has tried to engage you on this subject
> on the list. I'm sorry we're not all ZOMGRUBYBLOCKS!!!!111 like the
> commenters on your project page.

Goddamit, did I miss a post somewhere? What the hell is this project
people keep talking about?



--
Steven

alex23

unread,
May 4, 2012, 12:26:29 AM5/4/12
to
On May 4, 1:47 pm, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> I too am uncomfortable about passing locals() to a function, but not
> because of imaginary "name clashes". The problem as I see it is that this
> will give the function access to things the function has no need for.

And I would never use it in the real world. If anything, I'd rebind
via the function parameters:

def f(arg,fn1=None,fn2=None): pass

f('arg', **locals())

This way, only the aspects of the local scope that the function
explicitly asks for are provided.

But: I would _only_ do this in a context I controlled. However, that
would be the _same_ context in which the code blocks example would
also be used. I think. I'm still waiting to see an example that is
clear. I've never _ever_ found myself thinking "this code would be a
LOT clearer if I didn't have to give it a name..."

> Another problem is that even in implementations where you can't rebind
> locals, the called function might mutate them instead. If any of the
> content of locals() are mutable, you're giving the function the potential
> to mutate them, whether it needs that power or not.

This is true. But that would be the case with a provided dict too. I
wasn't suggesting someone blindly throw locals into every function and
hope for the best. I was merely stating that if you know that your
function is only going to use certain values, it doesn't matter how
many values you pass it, if it chooses to ignore them.

> My argument is that this is equivalent to passing locals() as argument.
> Your local scope contains some arbitrary number of name bindings. Only
> some of them are actually used. Why pass all (say) 25 of them if the
> function only needs access to (say) three?

Flip it: I've set up a local scope that _only_ contains the functions
I need. Why manually create a dict, repeating the name of each
function as a key, when I can just use locals()?

> To me, passing locals() as an
> argument in this fashion is a code-smell: not necessary wrong or bad, but
> a hint that something unusual and slightly worrying is going on, and you
> should take a close look at it because there *may* be a problem.

Or, conversely, I _know_ what I'm doing in the context of my own code
and it's the most elegant way to write it.

Frankly, I don't really care; I'm sick of this whole thread. We're all
taking bullshit abstractions & toy examples and none of it is
indicative of how anyone would really write code.

> > So far, pretty much everyone who has tried to engage you on this subject
> > on the list. I'm sorry we're not all ZOMGRUBYBLOCKS!!!!111 like the
> > commenters on your project page.
>
> Goddamit, did I miss a post somewhere? What the hell is this project
> people keep talking about?

https://bitbucket.org/mtomassoli/codeblocks/

The examples here are a wonder to behold as well:
http://mtomassoli.wordpress.com/2012/04/20/code-blocks-in-python/

Ben Finney

unread,
May 4, 2012, 2:04:57 AM5/4/12
to
alex23 <wuw...@gmail.com> writes:

> The examples here are a wonder to behold as well:
> http://mtomassoli.wordpress.com/2012/04/20/code-blocks-in-python/

Wow. “What really happens is that rewrite rewrites the code, executes it
and quits.”

Please keep this far away from anything resembling Python.

--
\ “If you go flying back through time and you see somebody else |
`\ flying forward into the future, it's probably best to avoid eye |
_o__) contact.” —Jack Handey |
Ben Finney

alex23

unread,
May 4, 2012, 2:30:51 AM5/4/12
to
On May 4, 4:04 pm, Ben Finney <ben+pyt...@benfinney.id.au> wrote:
> Wow. “What really happens is that rewrite rewrites the code, executes it
> and quits.”

But that's the best bit! It's the one aspect of the module for which a
context manager would actually be applicable and there's not even a
suggestion of it :)



Kiuhnm

unread,
May 4, 2012, 7:12:59 AM5/4/12
to
It doesn't always produce name-clashes but it may do so.
Suppose that func takes some functions named fn1, fn2 and fn3. If you
only define fn2 but you forget that you already defined somewhere before
fn1, you inadvertently pass to func both fn1 and fn2.
Even worse, if you write
def a(): pass
def b(): pass
func(arg, locals())
and then you want to call func again with c() alone, you must write this:
def c(): pass
a = b = None
func(arg, locals())
Moreover, think what happens if you add a function whose name is equal
to that of a function accepted by func.
That's what I call name-clashing.
My solution avoids all these problems, promote encapsulation and let you
program in a more functional way which is more concise that the OOP way,
sometimes.

>> That's not the same thing. If a function accepts some optional
>> callbacks, and you call that function more than once, you will have
>> problems. You'll need to redefine some callbacks and remove others.
>> That's total lack of encapsulation.
>
> Hand-wavy, no real example, doesn't make sense.

Really? Then I don't know what would make sense to you.

>>> You haven't presented *any* good code or use cases.
>>
>> Says who? You and some others? Not enough.
>
> So far, pretty much everyone who has tried to engage you on this
> subject on the list. I'm sorry we're not all ZOMGRUBYBLOCKS!!!!111
> like the commenters on your project page.

It's impossible to have a constructive discussion while you and others
feel that way. You're so biased that you don't even see how biased you are.

>> The meaning is clear from the context.
>
> Which is why pretty much every post in this thread mentioned finding
> it confusing?
>
>> I would've come up with something even better if only Python wasn't so rigid.
>
> The inability for people to add 6 billion mini-DSLs to solve any
> stupid problem _is a good thing_. It makes Python consistent and
> predictable, and means I don't need to parse _the same syntax_ utterly
> different ways depending on the context.

If I and my group of programmers devised a good and concise syntax and
semantics to describe some applicative domain, then we would want to
translate that into the language we use.
Unfortunately, Python doesn't let you do that.
I also think that uniformity is the death of creativity. What's worse,
uniformity in language is also uniformity in thinking.
As I said in some other posts, I think that Python is a good language,
but as soon as you need to do something a little different or just
differently, it's a pain to work with.

>> Because that would reveal part of the implementation.
>> Suppose you have a complex visitor. The OOP way is to subclass, while
>> the FP way is to accept callbacks. Why the FP way? Because it's more
>> concise.
>> In any case, you don't want to reveal how the visitor walks the data
>> structure or, better, the user doesn't need to know about it.
>
> Again, nothing concrete, just vague intimations of your way being
> better.

Sigh.

>>> So define& use a different scope! Thankfully module level isn't the
>>> only one to play with.
>>
>> We can do OOP even in ASM, you know?
>
> ???

You can do whatever you want by hand: you can certainly define your
functions inside another function or a class, but that's just more noise
added to the mix.

>>> I'm sorry but it is still clear-as-mud what you're trying to show
>>> here. Can you show _one_ practical, real-world, non-toy example that
>>> solves a real problem in a way that Python cannot?
>>
>> I just did. It's just that you can't see it.
>
> "I don't understand this example, can you provide one." "I just did,
> you didn't understand it."

Your rephrasing is quite wrong. You asked for a practical example and I
said that I already showed you one. It's just that you can't see it (as
practical).

> Okay, done with this now. Your tautologies and arrogance are not
> clarifying your position at all, and I really don't give a damn, so
> *plonk*

I don't care if you don't read this post. Now that I've written it I'll
post it anyway.
Unfortunately, communication is a two-people thing. It's been clear from
the first post that your intention wasn't to understand what I'm proposing.
There are some things, like what I say about name-clashing, that you
should understand no matter how biased you are.
If you don't, you're just pretending or maybe you weren't listening at all.

Kiuhnm
0 new messages