why not try without except?

Hello, In various cases, we need to do something (set or update a var or attribute, launch an action, compute data), or not, according to a condition that is a potential case of exception. Python provides the try...except construct to allow straightforward expression of the non-exceptional case without overloading the code with explicit checkings. Still, in the common case above, the result is try: <do_something> except ErrorType: pass or we have to fall back to a pre-checking construct if not <error_case>: <do_something> For instance try: text += doc.footer except ErrorType: pass or if hasattr(doc, 'footer') text += doc.footer Actually, this shows that 'try' alone can be considered analog to 'if' without 'else' clause, needed when the condition may raise an exception. Both express an optional action. This could be written more directly, because we do not need the condition: ? <do_something> option <do_something> with the meaning: "Try & do this, but if there's an exception just let it down." The same syntax may be reused in other contexts such as for having optional parameters: def f(data, option param): def f(data, ? param): Actually, the meaning is the same: "Try & and read a second argument, but if you step on an exception just let it down." Moreover, the body of the func will probably use precisely the same construct to try and do something using the optional param, e.g.: class Circle: ....... def draw(self, ? fill_color): <draw outline> ? self.fill(fill_color) As a nice side-effect, this would also remove one of the common (mis?)uses of None -- which is often considered problematic because of conflicting various uses. I would then support the introduction of such a syntax, or of 'try' without 'except'. And sincerally thank you for explaining why the latter was not allowed from start. Denis ------ la vita e estrany

On Sat, Apr 25, 2009 at 10:32 AM, spir <denis.spir@free.fr> wrote:
-1 for making bad programming easier, but not good programming. One should only ignore exceptions if one is 100% sure why an error occured, if not, the fact that an error occured should somehow be noted. That is, try without except would be equivalent to the following bad programming: try: do_something except: pass Good programming would be either: try: do_something except some_specific_error: pass or try: do_something except: notify_that_error_occurred neither of which would be made easier by try-without-except. -- André Engels, andreengels@gmail.com

spir <denis.spir@free.fr> writes:
Actually, this shows that 'try' alone can be considered analog to 'if' without 'else' clause
And without a condition, or a branch. Remember that ‘if’ introduces a branch point, whereas ‘try’ does not.
needed when the condition may raise an exception.
But there *is* no condition in a ‘try’ statement. If what you mean is “when the suite introduced by ‘try:’ may raise an exception”, that's true of just about every interesting statement in Python: an exception might be raised at any point.
Both express an optional action.
No. The suite that follows ‘try’ will be executed, just as surely as a suite *not* enclosed in ‘try’.
What does “let it down” mean? If you mean “if there's an exception raised, just let it propagate up”, that's what happens *without* a ‘try’. So I can only assume you mean something different. I don't understand the behaviour you describe. Perhaps if you would explain what you think the difference should be between: wibble() try: foo() bar() baz() wobble() versus this: wibble() foo() bar() baz() wobble() How should the behaviour differ? -- \ “It takes a big man to cry, but it takes a bigger man to laugh | `\ at that man.” —Jack Handey | _o__) | Ben Finney

Le Sat, 25 Apr 2009 19:12:11 +1000, Ben Finney <ben+python@benfinney.id.au> s'exprima ainsi:
You're right, that's what I mean.
It's a question of point of view. From the language side, you're certainly right. But from the programmer side, I guess it's correct to say that "try... do_this... except ExceptionType... pass" expresses an optional action. Precisely because there is no alternative. It's like an if without else.
It means more or less: except Exception: pass
Yes, see above.
Well, i think my example circle drawing func answers your question. It does not propagate the exception, instead precisely avoids this. wibble() try: foo() bar() baz() wobble() <==> wibble() try: foo() bar() baz() except Exception: pass wobble() But I prefere the 'option' syntax not only because it expresses the intention in a clearler and more direct way; also because it applies on a single statement -- which avoids potential some misuse like may happen in your example: try with several statements can more easily catch unexpected errors -- but this happens with standard try...except too. Another example: result = ... option result.applyTransforms(transforms) <==> result = ... try: option result.applyTransforms(transforms) except Exception: pass Now, I agree with the critics in another reply that except Exception: pass is very different of except NameError: pass We could probably find a way to specify the type of exception: option result.applyTransforms(transforms) except NameError Denis ------ la vita e estrany

spir <denis.spir@free.fr> writes:
Well, i think my example circle drawing func answers your question. It does not propagate the exception, instead precisely avoids this.
Thank you for explaining. I am solidly against this proposal, as I think it's a pattern that should be discouraged, not accommodated.
Can you show some real-world code that would be improved by this? I don't know of any code where *all* exceptions, especially unexpected ones, should simply be thrown away. If there are specific exception types that should be discarded, it is better to list them explicitly, and make sure your code is very clear on *why* those specific exceptions are being discarded. If you want exceptions to be handled in a generic way, it is better to set up a custom exception handler. -- \ “If you are unable to leave your room, expose yourself in the | `\ window.” —instructions in case of fire, hotel, Finland | _o__) | Ben Finney

spir schrieb:
And while we're at it, let's introduce on error resume next: foo() bar() baz() Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

This should answer the question: http://www.developerfusion.com/code/4325/on-error-resume-next-considered-har... On Sat, Apr 25, 2009 at 6:05 PM, Steven D'Aprano <steve@pearwood.info>wrote:

Le Sun, 26 Apr 2009 11:05:56 +1000, Steven D'Aprano <steve@pearwood.info> s'exprima ainsi:
It is certainly sarcastic -- never mind -;) Georg could have saved some typing by reading comments and examples... What I had in mind (and commented), is precisely not a syntactic construct able to catch any exception for a whole block of statements. E.g. an example like option self.fill(self.fillColor) can only catch AttributeError for 'fillColor' or for 'fill'. Now I realise that what I miss is a kind of handy, clear, and not misuseable idiom for coping with undefined variables/attributes/parameters. Otherwise one needs to fall back to the reflexive-check-instead-of-try idiom: if hasattr(self,'fillColor'): self.fill(self.fillColor) or use try... except ... pass. 'None' is also often (mis)used for this. People aware of the issues with None will rather have a custom undef-meaning object instead. Right? UNDEF = object() But this also requires checking instead of trying, doesn't it? It also requires binding a default UNDEF value to names that, conceptually speaking, can well remain undefined (UNDEF is still a value, as well as None). This is rather close to the use of Haskell's optional type (I guess it's called 'Maybe' -- checked, see e.g. http://en.wikibooks.org/wiki/Haskell/Hierarchical_libraries/Maybe). And I think Scala has something similar, too. But these special types fit well the semantics of statically-typed and compile-time-checked languages. One issue with None is that it can be used as a valid value. A workaround may be a user-unsettable undef-meaning builtin value. Or a builtin isDef() function. Still, both of these methods require checking. While the 'option' (or '?') proposal allows trying-instead-of-checking. Why not restrict it to a set of sensible exception types? for instance (this is what I would like): option foo or ? foo <==> try: foo except (NameError, AttributeError): pass Moreover: Haskell's Maybe type can be used for return values. I really wonder about python action-functions (that do not produce a result) returning None instead of nothing at all. This is another story, indeed, but I see it closely related at the meaning level. searched = seq.find(....) # may not return anything, not even None option print searched Denis ------ la vita e estrany

Le Sun, 26 Apr 2009 19:05:01 +1000, Steven D'Aprano <steve@pearwood.info> s'exprima ainsi:
I understand your position, indeed. Mine is: optional things and optional actions are basic and common notions. Even more in modelizing / programming fields. Are they important enough (for you) to require a dedicated idiom in a (your favorite) PL? class Shape(object): def __init__(self, ..., ?fill_color) ....... ?self.fill_color = fill_color def show(self): ....... ?self.fill(self.fill_color) Moreover, imagine NoReturnValueError instead of None; raised by python when a func call is used as an expression. x = f() # may raise NoReturnValueError # will never bind None except explicitely returned This would first avoid numerous bugs, often difficult to catch because silent: y = x.sort() # awa! (*) (**) Also one could then write, for instance: def show(self): ....... ?self.fill(self.fill_color) ?self.showTansforms(user.getTransforms()) (if NoReturnValueError belongs to the set of error types caught by optional actions) OK, won't fight for this anyway ;-) denis (*) I would also love a way to prevent the opposite bug: s.strip() # awa! (**) Actually, I must be a fan of Pascal's procedure vs function distinction. Find it sensible and meaningful. ------ la vita e estrany

On Sun, 26 Apr 2009 07:52:40 pm spir wrote:
In this case, fill_colour shouldn't be optional. There are two better ways to deal with it. Here's one: class UnfilledShape(object): # no fill colour at all. ... class FilledShape(UnfilledShape): # has a fill colour ... or if you prefer: class Shape(object): def __init__(self, ..., fill_color=TRANSPARENT): ... Either solution is better than writing code that is filled with checks for missing values. Whether you manually check it yourself, or have syntax support, you still make the code more complicated. -- Steven D'Aprano

Le Sun, 26 Apr 2009 21:38:00 +1000, Steven D'Aprano <steve@pearwood.info> s'exprima ainsi:
I like your solutions and I sincerally thank you for them. Still, think at this: There are numerous methods to cope with such "optional things", such as your subclassing above, using None, using custom special object, checking before trying, try.. except.. pass, and so on. But all of them all are custom expressions of the "optional" concept. Which is not a weird notion at all: it's instead omni-present in everyday life as well as in many programs. As python and most PLs do not have any builtin idiom matching it, we certainly are not really aware of it (first myself, before it became clear thanks to this thread). Especially because we can use different constructs to cope with it, depending on the specific case, it does come up in a unique form. But now I am rather that if we had learnt python with it, then we would use it regularly. As for your remark "In this case, fill_colour shouldn't be optional.": yes, it should! Precisely, it's the good property of an optional parameter (in the model) to be optional (in the code). There is no natural law of CS that says that this is A-Bad-Thing. Instead it's always better to have the program better mirror what it is intended to express (*). Nothing will ever explode if I code the Shape class like above. Denis ------ (*) This my personal Zeroest Law Of Programming ;-) la vita e estrany

spir wrote:
option self.fill(self.fillColor) can only catch AttributeError for 'fillColor' or for 'fill'.
You mean that 'option' *only* catches AttributeError? What makes you think that AttributeError is the most common exception to want to catch in these kinds of situations? Seems to me you're just as likely to want to catch one of ValueError, IndexError, KeyError, etc. Also, I don't find your fillColor example very convincing. If I have a class that may or may not have a fillColor, I set the fillColor to None when it doesn't have one -- I don't just leave the fillColor attribute undefined. The code then becomes if self.fillColor: self.fill(self.fillColor) or if I want to minimise attribute lookups, color = self.fillColor if color: self.fill(color) -- Greg

spir schrieb:
It certainly sounded that way in the original post.
E.g. an example like option self.fill(self.fillColor) can only catch AttributeError for 'fillColor' or for 'fill'.
Or AttributeErrors raised inside of fill().
That depends. Usually you would always make "fillColor" an attribute of the object, and either let fill() accept None or not call it if fillColor is None.
No. None is exactly meant to indicate the absence of a meaningful value.
And in Python, None fills the role of Haskell's "Nothing" constructor. There is no need for a "Just" constructor, since we don't have static types.
One issue with None is that it can be used as a valid value.
Of course. Every object is a "valid value".
A workaround may be a user-unsettable undef-meaning builtin value. Or a builtin isDef() function.
It seems you should make sure you know how Python handles namespaces.
It is almost always an error if a local name is not defined. I've already covered the case of AttributeErrors above.
Functions implicitly returning None is just a convention. If you don't like the ambiguity between "procedures" returning None and "functions" returning None, define your own "not-valid" value and return it from functions. It won't result in code that is easier to read though.
What is "searched" supposed to be, then? "Not anything" is not an option. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

[off list] Le Sun, 26 Apr 2009 22:36:57 +0200, Georg Brandl <g.brandl@gmx.net> s'exprima ainsi:
Raise an exception, obviously (like I showed before). Either None [or any or word you like (nil?)] is, say "non-assignable" or rather non-usable in any kind of expression and get NoneError because you tried to use it. Or the func does not return anything, literally (like a pascal procedure), and you get NoReturnValueError for trying to use it. The only safe alternative is to have 2 kinds of funcs: action-funcs and production-funcs. My workarouns is to use always verbs for actions and nouns (naming the result) for productions. This issue is that in english the same word is often both a noun and a verb ;-) (moral: flexibility is not only an advantage) There is side-topic you evoke in your reply: << If you don't like the ambiguity between "procedures" returning None and "functions" returning None, define your own "not-valid" value and return it from functions. >> Actually, this was not my point as you can see above, still this issue also exists. But we have the proper solution for it. A production-func is intended to normally return a result; if it cannot do it, we face an "exceptional case" so just throw an exception to handle it. This is indeed very different of the issue caused by the fact that we have no mean to distinguish action-funcs. Code using or assigning the None result will run free, often fail somewhere else, thus causing usually hard-to-find bugs. You know it, for sure. The reason is that None is assignable. Consequentely, a func should not return it if not explicitely told to. Denis ------ la vita e estrany

On Sat, 25 Apr 2009 06:32:07 pm spir wrote:
I've had a look in my code, and I very rarely have pass in the except-block. I don't think this is any where near as common as you think.
What is ErrorType? If you mean AttributeError, say so :) A third alternative is to make sure that doc.footer always exists, even if it is only the empty string, and then just write: text += doc.footer In many cases, I prefer that. I don't like attributes which sometimes exist and sometimes don't.
Actually, this shows that 'try' alone can be considered analog to 'if' without 'else' clause,
I think that is stretching the analogy past breaking point. In the if...else case, only one of the if-block or else-block are taken. But that's not true in the case of try...except: potentially both blocks are taken. L = [] try: for x in ('1.2', '3.4', '5.6', '7..7'): print x L.append(float(x)) except ValueError: print '%s is not a valid float' % x finally: print L As you can see from running that code snippet, both the try and except blocks get run. And there is no analogy to finally in if...else.
But what exception? You have to specify what sort of exception(s) you want to ignore, because not all exceptions mean the same thing. Consider this: try: text += doc.footer # just ignore exceptions Do you really want to ignore ALL exceptions? I would say not! You want to ignore a *single* exception, AttributeError. You don't want to hide exceptions like NameError (text or doc don't exist) or TypeError (text and doc.footer can't be added), because they indicate bugs that should not be hidden. Nor do you want to catch exceptions like KeyboardInterrupt. Since the Python compiler can't read the programmer's mind and know what exceptions to catch or not to catch, there are only three alternatives: (1) The current situation: you *must* specify the exception-clause. (2) Ignore *all* exceptions. This will hide bugs, causing them to crop up later when they will be harder to debug. It will also make the program un-interruptable by catching KeyboardInterrupt. (3) Ignore some sub-set of exceptions, like Exception. But again, this will still catch exceptions you didn't intend, and possibly let through exceptions you wanted to catch.
First of all, I think that this will allow a style of programming which is lazy and undisciplined and that will lead to more bugs, not fewer. If param is *really* optional, then you are probably dealing with something best written as two different functions: def f_without_param(data): def f_with_param(data, param): But for the sake of the argument, let's accept that we should write the code with an optional argument in that way. Now what happens? Firstly, every time you refer to param, you need to prefix the line with '?' because param may or may not exist. This is inconvenient and will lead to bugs when you forget to include a '?' at the start of the line. Worse, it will lead to more bugs: def f(data, ? prefix): ? data = prefix + dat # oops a typo print data f("visible", "in") => prints "visible" What is the interaction between "optional" param and namespaces? Consider: def f(? param): ? print param f() # I expect this will do nothing, swallowing the NameError exception param = "spam" f(param) # I expect this to print "spam" f() # What will this do? The third case is interesting: should f() print "spam" because param exists in the global namespace, or should it do nothing because the local argument param doesn't exist? Whichever choice you make, it will make some people unhappy. -- Steven D'Aprano

The idea of making it easy blindly ignore all exceptions is not very interesting. On Sat, Apr 25, 2009 at 7:25 PM, Steven D'Aprano <steve@pearwood.info>wrote:
It is a common case though and convenient to have a simple way to resolve it. Adding a statement level ? operator would be a mistake in my opinion because it clobbers the entire statement rather than just the specific. If only :-) there were a way to get an attribute while at the same time specifying the default value if the attribute didn't exist, say something like: getattr(var, attribute [, default] ) Of course, I'd also it would make sense to also have: getitem(var, index [, default] ) --- Bruce

On Sun, 26 Apr 2009 03:30:47 pm Bruce Leban wrote:
But the OP isn't suggesting default values. See, for example, his discussion of ignoring any errors when the user doesn't supply a parameter to a function. If all he wanted was a default value, we can do that already.
Of course, I'd also it would make sense to also have:
getitem(var, index [, default] )
Hmmm... interesting... Of course you can already do this: var[index:index+1] or [default] if var is a list, and var.get(key, default) if it is a dictionary. -- Steven D'Aprano

On Sat, Apr 25, 2009 at 10:32 AM, spir <denis.spir@free.fr> wrote:
-1 for making bad programming easier, but not good programming. One should only ignore exceptions if one is 100% sure why an error occured, if not, the fact that an error occured should somehow be noted. That is, try without except would be equivalent to the following bad programming: try: do_something except: pass Good programming would be either: try: do_something except some_specific_error: pass or try: do_something except: notify_that_error_occurred neither of which would be made easier by try-without-except. -- André Engels, andreengels@gmail.com

spir <denis.spir@free.fr> writes:
Actually, this shows that 'try' alone can be considered analog to 'if' without 'else' clause
And without a condition, or a branch. Remember that ‘if’ introduces a branch point, whereas ‘try’ does not.
needed when the condition may raise an exception.
But there *is* no condition in a ‘try’ statement. If what you mean is “when the suite introduced by ‘try:’ may raise an exception”, that's true of just about every interesting statement in Python: an exception might be raised at any point.
Both express an optional action.
No. The suite that follows ‘try’ will be executed, just as surely as a suite *not* enclosed in ‘try’.
What does “let it down” mean? If you mean “if there's an exception raised, just let it propagate up”, that's what happens *without* a ‘try’. So I can only assume you mean something different. I don't understand the behaviour you describe. Perhaps if you would explain what you think the difference should be between: wibble() try: foo() bar() baz() wobble() versus this: wibble() foo() bar() baz() wobble() How should the behaviour differ? -- \ “It takes a big man to cry, but it takes a bigger man to laugh | `\ at that man.” —Jack Handey | _o__) | Ben Finney

Le Sat, 25 Apr 2009 19:12:11 +1000, Ben Finney <ben+python@benfinney.id.au> s'exprima ainsi:
You're right, that's what I mean.
It's a question of point of view. From the language side, you're certainly right. But from the programmer side, I guess it's correct to say that "try... do_this... except ExceptionType... pass" expresses an optional action. Precisely because there is no alternative. It's like an if without else.
It means more or less: except Exception: pass
Yes, see above.
Well, i think my example circle drawing func answers your question. It does not propagate the exception, instead precisely avoids this. wibble() try: foo() bar() baz() wobble() <==> wibble() try: foo() bar() baz() except Exception: pass wobble() But I prefere the 'option' syntax not only because it expresses the intention in a clearler and more direct way; also because it applies on a single statement -- which avoids potential some misuse like may happen in your example: try with several statements can more easily catch unexpected errors -- but this happens with standard try...except too. Another example: result = ... option result.applyTransforms(transforms) <==> result = ... try: option result.applyTransforms(transforms) except Exception: pass Now, I agree with the critics in another reply that except Exception: pass is very different of except NameError: pass We could probably find a way to specify the type of exception: option result.applyTransforms(transforms) except NameError Denis ------ la vita e estrany

spir <denis.spir@free.fr> writes:
Well, i think my example circle drawing func answers your question. It does not propagate the exception, instead precisely avoids this.
Thank you for explaining. I am solidly against this proposal, as I think it's a pattern that should be discouraged, not accommodated.
Can you show some real-world code that would be improved by this? I don't know of any code where *all* exceptions, especially unexpected ones, should simply be thrown away. If there are specific exception types that should be discarded, it is better to list them explicitly, and make sure your code is very clear on *why* those specific exceptions are being discarded. If you want exceptions to be handled in a generic way, it is better to set up a custom exception handler. -- \ “If you are unable to leave your room, expose yourself in the | `\ window.” —instructions in case of fire, hotel, Finland | _o__) | Ben Finney

spir schrieb:
And while we're at it, let's introduce on error resume next: foo() bar() baz() Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

This should answer the question: http://www.developerfusion.com/code/4325/on-error-resume-next-considered-har... On Sat, Apr 25, 2009 at 6:05 PM, Steven D'Aprano <steve@pearwood.info>wrote:

Le Sun, 26 Apr 2009 11:05:56 +1000, Steven D'Aprano <steve@pearwood.info> s'exprima ainsi:
It is certainly sarcastic -- never mind -;) Georg could have saved some typing by reading comments and examples... What I had in mind (and commented), is precisely not a syntactic construct able to catch any exception for a whole block of statements. E.g. an example like option self.fill(self.fillColor) can only catch AttributeError for 'fillColor' or for 'fill'. Now I realise that what I miss is a kind of handy, clear, and not misuseable idiom for coping with undefined variables/attributes/parameters. Otherwise one needs to fall back to the reflexive-check-instead-of-try idiom: if hasattr(self,'fillColor'): self.fill(self.fillColor) or use try... except ... pass. 'None' is also often (mis)used for this. People aware of the issues with None will rather have a custom undef-meaning object instead. Right? UNDEF = object() But this also requires checking instead of trying, doesn't it? It also requires binding a default UNDEF value to names that, conceptually speaking, can well remain undefined (UNDEF is still a value, as well as None). This is rather close to the use of Haskell's optional type (I guess it's called 'Maybe' -- checked, see e.g. http://en.wikibooks.org/wiki/Haskell/Hierarchical_libraries/Maybe). And I think Scala has something similar, too. But these special types fit well the semantics of statically-typed and compile-time-checked languages. One issue with None is that it can be used as a valid value. A workaround may be a user-unsettable undef-meaning builtin value. Or a builtin isDef() function. Still, both of these methods require checking. While the 'option' (or '?') proposal allows trying-instead-of-checking. Why not restrict it to a set of sensible exception types? for instance (this is what I would like): option foo or ? foo <==> try: foo except (NameError, AttributeError): pass Moreover: Haskell's Maybe type can be used for return values. I really wonder about python action-functions (that do not produce a result) returning None instead of nothing at all. This is another story, indeed, but I see it closely related at the meaning level. searched = seq.find(....) # may not return anything, not even None option print searched Denis ------ la vita e estrany

Le Sun, 26 Apr 2009 19:05:01 +1000, Steven D'Aprano <steve@pearwood.info> s'exprima ainsi:
I understand your position, indeed. Mine is: optional things and optional actions are basic and common notions. Even more in modelizing / programming fields. Are they important enough (for you) to require a dedicated idiom in a (your favorite) PL? class Shape(object): def __init__(self, ..., ?fill_color) ....... ?self.fill_color = fill_color def show(self): ....... ?self.fill(self.fill_color) Moreover, imagine NoReturnValueError instead of None; raised by python when a func call is used as an expression. x = f() # may raise NoReturnValueError # will never bind None except explicitely returned This would first avoid numerous bugs, often difficult to catch because silent: y = x.sort() # awa! (*) (**) Also one could then write, for instance: def show(self): ....... ?self.fill(self.fill_color) ?self.showTansforms(user.getTransforms()) (if NoReturnValueError belongs to the set of error types caught by optional actions) OK, won't fight for this anyway ;-) denis (*) I would also love a way to prevent the opposite bug: s.strip() # awa! (**) Actually, I must be a fan of Pascal's procedure vs function distinction. Find it sensible and meaningful. ------ la vita e estrany

On Sun, 26 Apr 2009 07:52:40 pm spir wrote:
In this case, fill_colour shouldn't be optional. There are two better ways to deal with it. Here's one: class UnfilledShape(object): # no fill colour at all. ... class FilledShape(UnfilledShape): # has a fill colour ... or if you prefer: class Shape(object): def __init__(self, ..., fill_color=TRANSPARENT): ... Either solution is better than writing code that is filled with checks for missing values. Whether you manually check it yourself, or have syntax support, you still make the code more complicated. -- Steven D'Aprano

Le Sun, 26 Apr 2009 21:38:00 +1000, Steven D'Aprano <steve@pearwood.info> s'exprima ainsi:
I like your solutions and I sincerally thank you for them. Still, think at this: There are numerous methods to cope with such "optional things", such as your subclassing above, using None, using custom special object, checking before trying, try.. except.. pass, and so on. But all of them all are custom expressions of the "optional" concept. Which is not a weird notion at all: it's instead omni-present in everyday life as well as in many programs. As python and most PLs do not have any builtin idiom matching it, we certainly are not really aware of it (first myself, before it became clear thanks to this thread). Especially because we can use different constructs to cope with it, depending on the specific case, it does come up in a unique form. But now I am rather that if we had learnt python with it, then we would use it regularly. As for your remark "In this case, fill_colour shouldn't be optional.": yes, it should! Precisely, it's the good property of an optional parameter (in the model) to be optional (in the code). There is no natural law of CS that says that this is A-Bad-Thing. Instead it's always better to have the program better mirror what it is intended to express (*). Nothing will ever explode if I code the Shape class like above. Denis ------ (*) This my personal Zeroest Law Of Programming ;-) la vita e estrany

spir wrote:
option self.fill(self.fillColor) can only catch AttributeError for 'fillColor' or for 'fill'.
You mean that 'option' *only* catches AttributeError? What makes you think that AttributeError is the most common exception to want to catch in these kinds of situations? Seems to me you're just as likely to want to catch one of ValueError, IndexError, KeyError, etc. Also, I don't find your fillColor example very convincing. If I have a class that may or may not have a fillColor, I set the fillColor to None when it doesn't have one -- I don't just leave the fillColor attribute undefined. The code then becomes if self.fillColor: self.fill(self.fillColor) or if I want to minimise attribute lookups, color = self.fillColor if color: self.fill(color) -- Greg

spir schrieb:
It certainly sounded that way in the original post.
E.g. an example like option self.fill(self.fillColor) can only catch AttributeError for 'fillColor' or for 'fill'.
Or AttributeErrors raised inside of fill().
That depends. Usually you would always make "fillColor" an attribute of the object, and either let fill() accept None or not call it if fillColor is None.
No. None is exactly meant to indicate the absence of a meaningful value.
And in Python, None fills the role of Haskell's "Nothing" constructor. There is no need for a "Just" constructor, since we don't have static types.
One issue with None is that it can be used as a valid value.
Of course. Every object is a "valid value".
A workaround may be a user-unsettable undef-meaning builtin value. Or a builtin isDef() function.
It seems you should make sure you know how Python handles namespaces.
It is almost always an error if a local name is not defined. I've already covered the case of AttributeErrors above.
Functions implicitly returning None is just a convention. If you don't like the ambiguity between "procedures" returning None and "functions" returning None, define your own "not-valid" value and return it from functions. It won't result in code that is easier to read though.
What is "searched" supposed to be, then? "Not anything" is not an option. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

[off list] Le Sun, 26 Apr 2009 22:36:57 +0200, Georg Brandl <g.brandl@gmx.net> s'exprima ainsi:
Raise an exception, obviously (like I showed before). Either None [or any or word you like (nil?)] is, say "non-assignable" or rather non-usable in any kind of expression and get NoneError because you tried to use it. Or the func does not return anything, literally (like a pascal procedure), and you get NoReturnValueError for trying to use it. The only safe alternative is to have 2 kinds of funcs: action-funcs and production-funcs. My workarouns is to use always verbs for actions and nouns (naming the result) for productions. This issue is that in english the same word is often both a noun and a verb ;-) (moral: flexibility is not only an advantage) There is side-topic you evoke in your reply: << If you don't like the ambiguity between "procedures" returning None and "functions" returning None, define your own "not-valid" value and return it from functions. >> Actually, this was not my point as you can see above, still this issue also exists. But we have the proper solution for it. A production-func is intended to normally return a result; if it cannot do it, we face an "exceptional case" so just throw an exception to handle it. This is indeed very different of the issue caused by the fact that we have no mean to distinguish action-funcs. Code using or assigning the None result will run free, often fail somewhere else, thus causing usually hard-to-find bugs. You know it, for sure. The reason is that None is assignable. Consequentely, a func should not return it if not explicitely told to. Denis ------ la vita e estrany

On Sat, 25 Apr 2009 06:32:07 pm spir wrote:
I've had a look in my code, and I very rarely have pass in the except-block. I don't think this is any where near as common as you think.
What is ErrorType? If you mean AttributeError, say so :) A third alternative is to make sure that doc.footer always exists, even if it is only the empty string, and then just write: text += doc.footer In many cases, I prefer that. I don't like attributes which sometimes exist and sometimes don't.
Actually, this shows that 'try' alone can be considered analog to 'if' without 'else' clause,
I think that is stretching the analogy past breaking point. In the if...else case, only one of the if-block or else-block are taken. But that's not true in the case of try...except: potentially both blocks are taken. L = [] try: for x in ('1.2', '3.4', '5.6', '7..7'): print x L.append(float(x)) except ValueError: print '%s is not a valid float' % x finally: print L As you can see from running that code snippet, both the try and except blocks get run. And there is no analogy to finally in if...else.
But what exception? You have to specify what sort of exception(s) you want to ignore, because not all exceptions mean the same thing. Consider this: try: text += doc.footer # just ignore exceptions Do you really want to ignore ALL exceptions? I would say not! You want to ignore a *single* exception, AttributeError. You don't want to hide exceptions like NameError (text or doc don't exist) or TypeError (text and doc.footer can't be added), because they indicate bugs that should not be hidden. Nor do you want to catch exceptions like KeyboardInterrupt. Since the Python compiler can't read the programmer's mind and know what exceptions to catch or not to catch, there are only three alternatives: (1) The current situation: you *must* specify the exception-clause. (2) Ignore *all* exceptions. This will hide bugs, causing them to crop up later when they will be harder to debug. It will also make the program un-interruptable by catching KeyboardInterrupt. (3) Ignore some sub-set of exceptions, like Exception. But again, this will still catch exceptions you didn't intend, and possibly let through exceptions you wanted to catch.
First of all, I think that this will allow a style of programming which is lazy and undisciplined and that will lead to more bugs, not fewer. If param is *really* optional, then you are probably dealing with something best written as two different functions: def f_without_param(data): def f_with_param(data, param): But for the sake of the argument, let's accept that we should write the code with an optional argument in that way. Now what happens? Firstly, every time you refer to param, you need to prefix the line with '?' because param may or may not exist. This is inconvenient and will lead to bugs when you forget to include a '?' at the start of the line. Worse, it will lead to more bugs: def f(data, ? prefix): ? data = prefix + dat # oops a typo print data f("visible", "in") => prints "visible" What is the interaction between "optional" param and namespaces? Consider: def f(? param): ? print param f() # I expect this will do nothing, swallowing the NameError exception param = "spam" f(param) # I expect this to print "spam" f() # What will this do? The third case is interesting: should f() print "spam" because param exists in the global namespace, or should it do nothing because the local argument param doesn't exist? Whichever choice you make, it will make some people unhappy. -- Steven D'Aprano

The idea of making it easy blindly ignore all exceptions is not very interesting. On Sat, Apr 25, 2009 at 7:25 PM, Steven D'Aprano <steve@pearwood.info>wrote:
It is a common case though and convenient to have a simple way to resolve it. Adding a statement level ? operator would be a mistake in my opinion because it clobbers the entire statement rather than just the specific. If only :-) there were a way to get an attribute while at the same time specifying the default value if the attribute didn't exist, say something like: getattr(var, attribute [, default] ) Of course, I'd also it would make sense to also have: getitem(var, index [, default] ) --- Bruce

On Sun, 26 Apr 2009 03:30:47 pm Bruce Leban wrote:
But the OP isn't suggesting default values. See, for example, his discussion of ignoring any errors when the user doesn't supply a parameter to a function. If all he wanted was a default value, we can do that already.
Of course, I'd also it would make sense to also have:
getitem(var, index [, default] )
Hmmm... interesting... Of course you can already do this: var[index:index+1] or [default] if var is a list, and var.get(key, default) if it is a dictionary. -- Steven D'Aprano
participants (8)
-
Andre Engels
-
Arnaud Delobelle
-
Ben Finney
-
Bruce Leban
-
Georg Brandl
-
Greg Ewing
-
spir
-
Steven D'Aprano