Can global variable be passed into Python function?

S

Steven D'Aprano

I also took a look at the proposals. I don't think it's the editor
issue. The variant I proposed most recently:

with self.state from Connection.State:
if CONNECTING or CONNECTED:
...
elif DISONNECTING:
...
else:
...

would be handled gracefully by all sane python editors, I believe.

The main problem is that it can't be optimized effectively without
bringing in an element of preprocessing.

You can't have it both ways: you cannot claim that switch or case is more
readable than a chain of if...elif, and then propose a syntax which is
effectively a chain of if...elif while still claiming it is an
improvement. It's not. All you've done is introduce an element of
implicit magic to the syntax.


A dict dispatch table is just awful. At least have the decency of
creating inner classes.

Why (how?) would you want to use *multiple* classes for a single switch?

Dict dispatch tables are elegant, attractive and efficient if you are
using pre-existing functions or functions you can create using lambda:

dispatch = {
BLUE: handle_blue,
RED: lambda arg: process(arg, this, that),
...
}

dispatch[key](something)

It gets unwieldy if the functions don't already exist and cannot be
embedded as a single expression. In that case, there are two simple
approaches for when your switch cases are strings:

- move the functions into a class, as self-less methods, then use
the class __dict__ as the dispatch table;

- or move the functions into an external module, and use the module
__dict__ as the dispatch table.


Even when you're not switching on strings, with the aid of a simple
helper function, it's trivial:

dispatch = {}
def case(*args):
def decorator(func):
for a in arg:
dispatch[a] = func
return func
return decorator


class Dispatch:
@case(BLUE)
def handle_blue(something):
...

@case(RED):
def handle_red(something):
...


dispatch[BLUE](something)


There are all sorts of simple and neat ways to handle switches in Python.
That's why the pressure on the language to grow special syntax is quite
low. It simply isn't important enough.
 
M

Marko Rauhamaa

Mark Lawrence said:

Your brief summary, please, Mark?

Anyway, the first 1000 lines or so that I managed to read from that page
stated a valid principle, which however doesn't invalidate the existence
of a switch statement.

A colleague of mine taught me decades back that the whole point of OO
was the avoidance of if and switch statements. So if your code has an if
or switch statement, chances are you are doing something wrong.

I agree.

However, like all other maxims, that principle, too, has exceptions. Two
recurring examples are parsers/decoders and state machines. Sure, you
can implement states beautifully with objects/classes (and I do do that
when performance is not an issue), but having experimented with
different implementation techniques (in C, C++ and Java), I have
concluded that switch statements are often unbeatable in performance and
clarity.

And I sometimes run into convoluted factory (anti)patterns whose sole
purpose is to avoid straightforward switch statements in a decoder.


Marko
 
S

Steven D'Aprano

Your example:
[snip]

I'm not going to show the example as quoted by you, because my news
client is wrapping it in ugly ways and I don't want to spend the time
fixing it. I'll just say that, as given, it's a great big wall of text, a
solid and ugly block, but can be easily fixed to be more readable and
look more like a switch with a judicious amount of indentation:

compare_key = {
ast.Assign:
# Same target(s).
lambda node: ' '.join(dump(t) for t in node.targets),
ast.AugAssign:
# Same target and same operator.
lambda node: dump(node.target) + dump(node.op) + "=",
ast.Return:
# A return statement is always compatible with another.
lambda node: "(easy)",
ast.Expr:
# Calling these never compatible is wrong. Calling them
# always compatible will give lots of false positives.
lambda node: "(maybe)",
}

which compares favourably with your version:

with key from ast:
if Assign:
return ' '.join(dump(t) for t in node.targets)
elif AugAssign:
# Same target and same operator.
return dump(node.target) + dump(node.op) + "="
elif Return:
# A return statement is always compatible with another.
return "(easy)"
elif Expr:
# Calling these never compatible is wrong. Calling them
# always compatible will give lots of false positives.
return "(maybe)"


I've deliberately dropped out "else" clause, because the two code
snippets as shown by you don't do the same thing. The dict dispatch table
shown initially lists the remaining keys allowed:

ast.Import, ast.ImportFrom, ast.Pass, ast.Raise, ast.If

and sets them all individually to five different but equivalent
functions, lambda node: float("nan"). This is both good and bad: good,
because it enumerates precisely what keys are expected, and will fail if
given an unexpected value; bad because it requires repeating yourself
five times to give the five keys the same function:

ast.Import:
lambda node: float("nan"),
ast.ImportFrom:
lambda node: float("nan"),

etc. (Actually five different functions which happen to do exactly the
same thing.) On the other hand, your with...if...elif version simply uses
a single "else" statement, which means it will accept *any other value at
all*, not just the five keys accepted by Chris' version. So there is a
semantic difference between the two.

In practice, sometimes you want a strict dispatch table that raises an
error when given something unexpected, other times nothing is unexpected
and you want an "else" or "otherwise" clause that captures everything.
There are all sorts of ways to handle this in practice, e.g. using
defaultdict, or subclassing dict and giving it a __missing__ method, or
simply deal with the default case outside of the table itself:

# instead of this
dispatch[key](node)

# do this
dispatch.get(key, lambda node: float('nan'))(node)

Which do *you* find more readable?

With proper code layout, the dict-based dispatch table is at least as
readable, and it avoids needing any magic compiler support.
 
M

Mark H. Harris


... and between me and you, here is a snip from dmath.py from the atan(x) function:

if (n**2 < D(1)):
a = __atan__(n)
elif (n == D(1)):
a = gpi/4
elif (n == D(-1)):
a = -(gpi/4)
elif (n < D(-1)):
a = __atan__Lt_neg1__(n)
else:
a = __atan__Gt_1__(n)

This if--elif--else is not only ugly, its just not readable either, andbesides that, its not elegant, nor is it humanly helpful... its does work though, and its absolutely necessary. ugh.

First, its not immediately clear what it does. Well, there isn't just one atan(x) routine, there are at least four of them, depending on whether you're a purist, and they must be selected.

Second, because of the strict intent ideology of python in the first place, I can't indent this code to make it more readable without breaking python's syntax.

Third, this is a VERY simple if elif block. More complex ones are much worse... for human reading that is...

I know its a pain in the neck, but python does need a switch statement.. Is it a stubborn question? I don't really think that almost every modern computer language has a switch block because of some C paradigm. I thinkits because most coders find them useful, at least readable, and thereforeessential.

On the other hand, I have coded many scripts and lots of commercial software that used no switch blocks at all... particularly my C++ and Java stuff.

Sooo...
 
R

Roy Smith

Mark H. Harris said:
... and between me and you, here is a snip from dmath.py from the atan(x)
function:

if (n**2 < D(1)):
a = __atan__(n)
elif (n == D(1)):
a = gpi/4
elif (n == D(-1)):
a = -(gpi/4)
elif (n < D(-1)):
a = __atan__Lt_neg1__(n)
else:
a = __atan__Gt_1__(n)

This if--elif--else is not only ugly, its just not readable either, and
besides that, its not elegant, nor is it humanly helpful... its does
work though, and its absolutely necessary. ugh.

First, its not immediately clear what it does. Well, there isn't just one
atan(x) routine, there are at least four of them, depending on whether
you're a purist, and they must be selected.

This kind of stuff is pretty common in numerical code. Depending on the
sign/magnitude/quadrant/whatever of the argument, you'll want to use one
of several algorithms to minimize roundoff error, etc.

But, how would this be any nicer with switch?
 
M

Marko Rauhamaa

Steven D'Aprano said:
You can't have it both ways: you cannot claim that switch or case is
more readable than a chain of if...elif, and then propose a syntax
which is effectively a chain of if...elif while still claiming it is
an improvement. It's not. All you've done is introduce an element of
implicit magic to the syntax.

You may be right that Python has too many practical problems for a truly
fitting switch syntax.
Why (how?) would you want to use *multiple* classes for a single
switch?

Well, I was just given a whole link on the very topic:

<URL: http://c2.com/cgi/wiki?SwitchStatementsSmell>

Here's a shorter one:

<URL: http://en.wikipedia.org/wiki/State_pattern>


Anyway, while implementing states as singleton inner class instances is
elegant and highly readable, the technique suffers in one significant
respect: it can be very slow. Often a state machine has numerous states
but the typical execution path only visits a few of them. However, the
pattern calls for declaring classes (or instances) for all states in the
state machine constructor. Switch statements make it possible to avoid
this creation overhead.
Dict dispatch tables are elegant, attractive and efficient if you are
using pre-existing functions or functions you can create using lambda:

I beg to differ. The dict dispatch tables are very hard to read. The
fully blown-out if-else chain beats it in elegance hands down.
There are all sorts of simple and neat ways to handle switches in
Python. That's why the pressure on the language to grow special syntax
is quite low. It simply isn't important enough.

Of course the threshold for new syntax should be exceptionally high.
Python as a language is already close to the sweet spot.


Marko
 
M

Marko Rauhamaa

Ben Finney said:
As has been pointed out to you, the whole point here is that string
objects often *are not* distinct, despite conceptually having distinct
cretion in the source.

You know full well that this initialization creates references to
distinct objects:

class ABC:
IDLE = "IDLE"
CONNECTING = "CONNECTING"
CONNECTED = "CONNECTED"
DISCONNECTING = "DISCONNECTING"
DISCONNECTED = "DISCONNECTED"

The 5 constants can (and should) be distinguished with the "is"
operator. Using "==" in this case would be slightly misleading.

You could define:

class ABC:
IDLE = None
CONNECTING = 1
CONNECTED = list
DISCONNECTING = MyDisconnecting(9)
DISCONNECTED = float("nan")

without changing the functionality of the state machine (as long as "is"
is used to identify the state).


Marko
 
S

Steven D'Aprano

The main reason to use "is" is to indicate that the object is a sentinel
object whose identity is what is meaningful, not the content. Using ==
would work but would give some poor soul the idea that the state
variable could hold any imaginable string.

Why should identity be important for testing the state of a state
variable?

There is a good reason for using "is" with actual sentinel values,
because we wish to protect against the (rare) situation where some other
object happens to compare equal to our sentinel. But why should states be
treated as sentinels instead of ordinary values where identity is not
important?

The most important reason to *not* use "is" is that doing so *implicitly*
exposes an implementation detail, namely that each state is a singleton.

Identity shouldn't be that important. Why does it matter that there is
only a single instance of the state INIT (say)? Why shouldn't an
implementation feel free to create and discard as many instances as
needed? Whether there is one INIT instance or a million is an
implementation detail that shouldn't matter to the user, they should be
able to just write "if state == INIT" and get the right answer.

The implementation should be able to change the state objects to any
(distinct) objects at all (say, the new enums, or ints, or some more
elaborate class instances) without any changes in the code.

In practice, this is often not true. Often you have compatibility
requirements with (say) external libraries or protocols, where the states
are set by value. E.g. you may have to be compatible with a C library
where the states are given as ints.

But more importantly, you don't actually have that much freedom to change
the states. Common sense[1] tells us that we're not really free to
replace states with arbitrary objects, they should be values that match
the name of the state. Given a state BLUE, we want inspecting it in a
debugger, or printing it, to display BLUE, not sBxvGe74sk or 23 or
<object object at 0xb7c1a568> and certainly not YELLOW.

After all, the states are *symbols*, which means they don't really have
any (internal) state or behaviour apart from their name. (In Python, we
have to given them a value, we can't just refer to name BLUE without
giving it a value, but the value isn't important. What we really care
about is the name.

Fundamentally, there's not terribly much difference between an API that
says:

"The colour variable can be BLUE or YELLOW"

versus one that says:

"The colour variable can be 'BLUE' or 'YELLOW'"


In both cases, you can't change the public API. Adding or subtracting a
couple of quotation marks is not a big deal (although perhaps it is a
small deal).

Although it may seem at first that by exposing the implementation
("states are given by strings") you're limiting your freedom to change
the implementation, in practice you don't actually have that much freedom
to do so, and very likely you're never[2] going to use that freedom
anyway. States are symbols, and rarely need behaviour apart from
equality, so why would you bother to change the implementation?

(If this sounds like a mild argument against Enums, I guess it is. After
all, Python worked quite well for 20+ years without Enums. Using strings
as "poor man's enums" works well enough. That's why it took so long for
Python to get "proper" enums, and even then, they aren't a core feature
of the language.)




[1] Common sense: so rare it is practically a super power.

[2] Well, hardly ever.
 
M

Marko Rauhamaa

Mark H. Harris said:
if (n**2 < D(1)):
a = __atan__(n)
elif (n == D(1)):
a = gpi/4
elif (n == D(-1)):
a = -(gpi/4)
elif (n < D(-1)):
a = __atan__Lt_neg1__(n)
else:
a = __atan__Gt_1__(n)

Drop the outermost parentheses in the if statements. They add to the
clutter.

Anyway, how would you express the above in, say, Java?


Marko
 
N

Ned Batchelder

... and between me and you, here is a snip from dmath.py from the atan(x) function:

if (n**2 < D(1)):
a = __atan__(n)
elif (n == D(1)):
a = gpi/4
elif (n == D(-1)):
a = -(gpi/4)
elif (n < D(-1)):
a = __atan__Lt_neg1__(n)
else:
a = __atan__Gt_1__(n)

This if--elif--else is not only ugly, its just not readable either, and besides that, its not elegant, nor is it humanly helpful... its does work though, and its absolutely necessary. ugh.

First, its not immediately clear what it does. Well, there isn't just one atan(x) routine, there are at least four of them, depending on whether you're a purist, and they must be selected.

Second, because of the strict intent ideology of python in the first place, I can't indent this code to make it more readable without breaking python's syntax.

Third, this is a VERY simple if elif block. More complex ones are much worse... for human reading that is...

I know its a pain in the neck, but python does need a switch statement. Is it a stubborn question? I don't really think that almost every modern computer language has a switch block because of some C paradigm. I think its because most coders find them useful, at least readable, and therefore essential.

I don't understand: you show an if/elif chain that cannot be expressed
as a switch statement (because it uses < ), and then conclude that
Python needs a switch statement? That doesn't make any sense.
 
S

Steven D'Aprano

You may be right that Python has too many practical problems for a truly
fitting switch syntax.

I didn't make that claim. My claim is that your suggested syntax is no
real improvement over existing syntax.

Well, I was just given a whole link on the very topic:

<URL: http://c2.com/cgi/wiki?SwitchStatementsSmell>

No no no, you've deleted the context. I'm not talking about making a
switch statement unnecessary by using polymorphism. I was responding to
your claim that instead of using a dict, one should:

At least have the decency of creating inner classes.
[end quote]

I don't understand this. That's why I asked why and how. I can only
imagine you mean something like this:

class Outer:
class InnerOne:
...

class InnerTwo:
...

class InnerThree:
...


but I don't see how you get from that to the functionality of a dispatch
table, or what you're supposed to do with the nested classes.


That's not a way of implementing switching. That's a way of *avoiding*
switching. Despite the name, it has little to do with state machines.

Anyway, while implementing states as singleton inner class instances is
elegant and highly readable,

o_O

That's terribly, terribly inelegant. Why not use the class objects
themselves, instead of instantiating them? Even then, you haven't escaped
the use of a dict dispatch table, or a chain of if...elif. You've just
defined the symbols you use.

the technique suffers in one significant
respect: it can be very slow.

Particularly if you program Java in Python.


Often a state machine has numerous states
but the typical execution path only visits a few of them. However, the
pattern calls for declaring classes (or instances) for all states in the
state machine constructor. Switch statements make it possible to avoid
this creation overhead.

How do you avoid creating the classes or instances? If they haven't been
created, how can the compiler reference them?


I beg to differ. The dict dispatch tables are very hard to read. The
fully blown-out if-else chain beats it in elegance hands down.

Only if you lay out the dict badly. Poorly written code is ugly and hard
to read no matter what you do.
 
R

Roy Smith

Ben Finney said:
Of course. That's the point of describing something as a “code smellâ€:
it may have exceptions where the smell does not indicate an actual
problem, but those are not the normal case where the smell is
encountered. More often, it indicates a problem that should be fixed.

An apt analogy is refrigerator smell. Sometimes it means the leftovers
from 3 months ago have evolved into a sentient life form. Sometimes it
just means you've got a piece of Roquefort.
 
S

Steven D'Aprano

You know full well that this initialization creates references to
distinct objects:

class ABC:
IDLE = "IDLE"
CONNECTING = "CONNECTING"
CONNECTED = "CONNECTED"
DISCONNECTING = "DISCONNECTING"
DISCONNECTED = "DISCONNECTED"

The 5 constants can (and should) be distinguished with the "is"
operator. Using "==" in this case would be slightly misleading.

I disagree. You are focusing on identity, but identity is (usually) not
important. That's an implementation detail.

What we have here is the curse of the singleton design anti-pattern:

http://accu.org/index.php/journals/337

https://molecularmusings.wordpress.com/2011/11/11/singleton-is-an-anti-pattern/

Since the symbols IDLE, CONNECTING etc. don't have state apart from their
name, whether there is one or a million and one instances is irrelevant.
 
M

Marko Rauhamaa

Steven D'Aprano said:
I can only imagine you mean something like this:

class Outer:
class InnerOne:
...

class InnerTwo:
...

class InnerThree:
...

No, I mean this:

class StateMachine:
def __init__(self):
sm = self

class Idle:
def connect(self):
...
sm.set_state(Connecting)
...

class Connecting:
def poll(self):
if sm.sock....:
sm.set_state(Connected)

class Connected:
...

self.set_state(Idle)

def set_state(self, state):
self.state = state()

def connect(self):
self.state.connect()

def poll(self):
self.state.poll()

...
How do you avoid creating the classes or instances? If they haven't
been created, how can the compiler reference them?

The comparison is between three techniques:

1. dict dispatch -- considered barely readable by me

2. the state pattern (see "class StateMachine" above)

3. sentinel state objects with switches

What I said in the above paragraph is that state machines can be written
using the state pattern (thus avoiding the need for switch statements).
However, the pattern often consumes lots of time and space.


Marko
 
M

Mark Lawrence

I don't understand: you show an if/elif chain that cannot be expressed
as a switch statement (because it uses < ), and then conclude that
Python needs a switch statement? That doesn't make any sense.

What a sneaky trick, trying to confuse people here with mere *FACTS* :)
 
M

Mark H. Harris

I don't understand: you show an if/elif chain that cannot be expressed
as a switch statement (because it uses < ), and then conclude that
Python needs a switch statement? That doesn't make any sense.

Forgive me. I would rewrite the structure,

switch x:
case GT_1:
__atan__Gt_1__(x)
case LT_1:
__atan__Lt_1__(x)
case IS_1:
a = gpi/4
case IS_n1:
a = -gpi/4
default:
__atan__(x)

or somesuch... way better... yes, there are some issues, but all workable with some effort

marcus
 
M

Marko Rauhamaa

Ben Finney said:
They are *not* necessarily distinct from other strings with equal
value, defined elsewhere. That's what has been pointed out to you many
times.

That point is completely irrelevant. The state objects only need to be
distinct from each other. How do I know? I originally wrote the example.
So, either you care about these values being distinct from all others
because you want to compare them with ‘is’ (and so strings are a poor
choice);

Strings are as good objects for the purpose as any and have some nice
advantages during troubleshooting and logging.


Marko
 
M

Mark Lawrence

I beg to differ. The dict dispatch tables are very hard to read. The
fully blown-out if-else chain beats it in elegance hands down.

I think you're talking nonsense. I've been happily using dict dispatch
tables for years and, like Steven and presumably many others, find them
dead easy to read. Perhaps you should invest in a better optician
and/or glasses?
 
M

Mark Lawrence

That point is completely irrelevant. The state objects only need to be
distinct from each other. How do I know? I originally wrote the example.


Strings are as good objects for the purpose as any and have some nice
advantages during troubleshooting and logging.


Marko

How many more times do you have to be shot down before you give up? Or
do you intend coming back like the Phoenix, or worse still like our
resident unicode expert, to haunt us?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,078
Messages
2,570,570
Members
47,204
Latest member
MalorieSte

Latest Threads

Top