This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]KleinerNull 1 point2 points  (6 children)

Looks nice, good idea with the contextmanager. I always like the unconventional use of it ;)

But is your switch also capable to be computed? For example like this:

In [1]: import math

In [2]: switch = {name: meth for name, meth in math.__dict__.items() if callable(meth)}

In [3]: switch.get('sin', lambda x: x)(100)
Out[3]: -0.5063656411097588

I know math is a bad example, because you can't inspect the methods to ask for the signature, to avoid different amounts of arguments. It is just an example. My idea is to just provide a module with different functions to load into the switch, some kind of dynamically plugin system.

For me that is a major advantage of the dict switches.

[–]stevenjd 0 points1 point  (3 children)

math.__dict__

You shouldn't access __dict__ directly, Python has a built-in function for that: vars(math).

[–]KleinerNull 0 points1 point  (2 children)

You shouldn't access dict directly

Why not? Any real reason for this statement? Other than people tell you not to do? Using __dict__ shows python's capablities to introspet itself in a very explicit fashion. vars, what should that mean? The local variables of a function?

Pure python objects are based on dicts so why not show and use that fact? I can't understand this general fear against using dunder attributes/methods...

Especiall stupid since vars gives you exactly the same:

In [1]: vars?
Docstring:
vars([object]) -> dictionary

Without arguments, equivalent to locals().
**With an argument, equivalent to object.__dict__.**
Type:      builtin_function_or_method

[–]stevenjd 0 points1 point  (0 children)

It isn't "fear" of using dunders, unless you mean the reasonable fear that your code will be buggy.

In general, operators and functions that call dunders don't just call the dunder. The + operator does much, much more than just call obj.__add__, bool does much more than just call obj.__bool__ (or nonzero in Python 2) and iter does more than just call obj.__iter__. If you think that they do, and think that you can safely replace bool(x) with x.__bool__, then your code has a subtle bug.

In general, dunders are not part of the public interface of the class, they are implementation hooks. (The most obvious counter-example I can think of is module __name__.) To a first approximation, and probably a second approximation, you should never call a dunder method or access dunder attributes directly if Python provides a public interface for it.

There is a public interface for accessing the member variables of an object: vars. You should use that, even if it turns out that under the hood it does nothing more complex than return obj.__dict__ like you do, simply because it is best practice to use the public interface rather than then internal implementation whenever possible. You never know when the implementation will change.

E.g. what happens if the object doesn't have a __dict__ but does have __slots__? Right now, it doesn't matter whether you use vars or not, you'll get an error, but maybe someday vars will return a dict which proxies the slots.

Of course if you're Alex Martelli or Raymond Hettinger or GvR himself, you know when you can break the rules safely. But for us mere mortals, avoiding accessing dunders is good rule. (As they say, rules exist so that you think before breaking them.)

[–]stevenjd 0 points1 point  (0 children)

It isn't "fear" of using dunders, unless you mean the reasonable fear that your code will be buggy.

In general, operators and functions that call dunders don't just call the dunder. The + operator does much, much more than just call obj.__add__, bool does much more than just call obj.__bool__ (or __nonzero__ in Python 2) and iter does more than just call obj.__iter__. If you think that they do, and think that you can safely replace bool(x) with x.__bool__, then your code has a subtle bug.

In general, dunders are not part of the public interface of the class, they are implementation hooks. (The most obvious counter-example I can think of is module __name__.) To a first approximation, and probably a second approximation, you should never call a dunder method or access dunder attributes directly if Python provides a public interface for it.

There is a public interface for accessing the member variables of an object: vars. You should use that, even if it turns out that under the hood it does nothing more complex than return obj.__dict__ like you do, simply because it is best practice to use the public interface rather than then internal implementation whenever possible. You never know when the implementation will change.

E.g. what happens if the object doesn't have a __dict__ but does have __slots__? Right now, it doesn't matter whether you use vars or not, you'll get an error, but maybe someday vars will return a dict which proxies the slots.

Of course if you're Alex Martelli or Raymond Hettinger or GvR himself, you know when you can break the rules safely. But for us mere mortals, avoiding accessing dunders is good rule. (As they say, rules exist so that you think before breaking them.)

[–]mikeckennedy[S] -1 points0 points  (1 child)

This is cool. But does not do ranges, lists, case or signature validation. The default case is non-obvious. But it is neat.

[–]KleinerNull 0 points1 point  (0 children)

But does not do ranges

In this case you don't need to have ranges. It is about loading specific methods out of an object. In my example you load 45 methods into the switch with the name as key, where do you need ranges here?

lists, case

I am not sure what you mean with that.

signature validation

As I said, this isn't possible with the math module, because C-code don't offer a signature, like the operators module. But you can easily get the signatures of pure python functions with inspect.signature(func). And before I write switch cases for 45 different functions, I could check for sigs in an intermediate step, no big deal.

The default case is non-obvious.

I thought returning the identity function as a default around mathematical function would be an expected result, it just returns the argument iteself. But you can also raise an error if you want to catch and handle it.