Style question: Disambiguating number from list of numbers

A forum for general discussion of the Python programming language.

Style question: Disambiguating number from list of numbers

Postby rovf » Tue Dec 10, 2013 10:59 am

Supposte a function having a parameter data is supposed to act differently, depending on the type of data. In the concrete case, data can be eithe a non-negative number, or a list of 3 numbers. If it is a number, n, the function is supposed to behave as if the list [n,n,n] had been passed. Let's ignore for a moment, that error checking would make sense for those cases, where the parameter does not comply to either form. There are several ways I can distinguish between those cases. Here a few examples:

Code: Select all
type(data).__name__ == 'list'

type(data) == type(list())

type(data) == type([])

import types
isinstance(data,types.ListType)


Now my questions:

[*]Is one of those 4 alternatives clearly superior (or inferior) to the others, for whatever reason (correctness, convention, readability, portability,....)?
[*]Is there a better way to code this, without bloating the code?
rovf
 
Posts: 25
Joined: Fri Aug 16, 2013 4:35 pm

Re: Style question: Disambiguating number from list of numbe

Postby Mekire » Tue Dec 10, 2013 11:31 am

Of all those the last is the only one I would say is remotely acceptable.
Although I'm not sure why you don't write it:
Code: Select all
isinstance(data,list)

Honestly though they are all bad for this scenario.
Ask for forgiveness not permission. Generally type checking is not the way.
Try to treat the data one way. If you fail catch the exception and try another way.

-Mek
User avatar
Mekire
 
Posts: 988
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Style question: Disambiguating number from list of numbe

Postby rovf » Tue Dec 10, 2013 11:53 am

Mekire wrote:Of all those the last is the only one I would say is remotely acceptable.
Although I'm not sure why you don't write it:
Code: Select all
isinstance(data,list)



Because I overlooked that one when posting the question; incidentally this is the one I had used in the code ;-)

Mekire wrote:Honestly though they are all bad for this scenario.
Try to treat the data one way. If you fail catch the exception and try another way.

-Mek


So this would be kind of:

Code: Select all
try:
  data[0] # See whether it behaves like a list
except:
  # data is not a list
  data=[data]*3
# Now it is ensured that data is a list


This would allow data to be any other type, as long as it behaves like a list. Would you regard this as a good solution, or can you see further improvements?
rovf
 
Posts: 25
Joined: Fri Aug 16, 2013 4:35 pm

Re: Style question: Disambiguating number from list of numbe

Postby hrs » Tue Dec 10, 2013 9:59 pm

What do you mean by 'behaves like a list'? In your example a string would behave like a list. Maybe you want anything thats an iterable.
Code: Select all
import collections

if isinstance(data, collections.Iterable):
    ...
hrs
 
Posts: 86
Joined: Thu Feb 07, 2013 9:26 pm

Re: Style question: Disambiguating number from list of numbe

Postby micseydel » Wed Dec 11, 2013 1:49 am

rovf wrote:So this would be kind of:

Code: Select all
try:
  data[0] # See whether it behaves like a list
except:
  # data is not a list
  data=[data]*3
# Now it is ensured that data is a list

First, you should catch the exact exception you'd get rather than a blanket one. Here, you probably want to catch a TypeError. That said, if you were going to do something like just return data right after this then you wouldn't want to use exceptions. Secondly, asking for "forgiveness rather than permission" is typically the Pythonic way of doing things, but this might not be it.

For example, I saw code at work where a script checked if a folder existed, and if not created it. The problem? If someone else created the folder before you did, your creation attempt would raise an exception. Better to try to create it, catch an IOError or whatever it was, then move forward as necessary, possibly checking it exists but if not then you know it can't so you give up. (Or you could just assume it worked, since the traceback you'd get if not would be straightforward for technical folk).

From the example code you showed, maybe that's not what you want. What if you were just going to return the variable, not doing anything related to lists but needing to make sure you returned one? Then isinstance() would be what you want. (Well perhaps what you really want is to do what Mek suggested further up the stack, but this is often not a viable option if it requires a great deal of code be re-written.)

This would allow data to be any other type, as long as it behaves like a list. Would you regard this as a good solution, or can you see further improvements?

If you post a bit more of your code for the use case we can advise you better, if you aren't sure of what you want after my attempt at explaining. But typically we do adhere to duck typing principals. If it looks like a duck, sounds like a duck....
Join the #python-forum IRC channel on irc.freenode.net!

Please do not PM members regarding questions which are meant to be discussed publicly. The point of the forum is so that others can benefit from it. We don't want to help you over PMs or emails.
User avatar
micseydel
 
Posts: 1390
Joined: Tue Feb 12, 2013 2:18 am
Location: Mountain View, CA

Re: Style question: Disambiguating number from list of numbe

Postby rovf » Wed Dec 11, 2013 10:30 am

micseydel wrote:
rovf wrote:So this would be kind of:

Code: Select all
try:
  data[0] # See whether it behaves like a list
except:
  # data is not a list
  data=[data]*3
# Now it is ensured that data is a list

First, you should catch the exact exception you'd get rather than a blanket one. Here, you probably want to catch a TypeError.


I thought of this, but could it be something else besides TypeError? Well, data could be None, but in this case, we would proceed with [None]*3 , and this would be caught shortly after anyway.

micseydel wrote:If you post a bit more of your code for the use case we can advise you better, if you aren't sure of what you want after my attempt at explaining. But typically we do adhere to duck typing principals.


Adhering to duck typing is always a good idea, as I will be more flexible in how I can use my function later. This was also the reason why I liked the idea with the exception. Now to what I'm exactly doing (because, as you already might have guessed, the example code I posted was stripped down to the minimum necessary which I thought was needed to demonstrate the problem):

I have a function, which - as one parameter - expects a list of 3 items. Seeing it from the viewpoint of duck typing, it doesn't need to be a list, but can be anything which can be indexed with [0], [1] and [2]. No other properties of a list are needed here. In practice, the actual parameter is currently always a list.

There are several points in the application, where this function is called with a list, where by design all 3 elements must have the same value. It would be convenient to allow here to write only one value here. I thought of three solutions for this:

(a) Have the caller write this explicitly, for example as
Code: Select all
myFunction([myScalarValue]*3,....)


(b) Provide a (differently-named) cover function, which expects a scalar, turns this scalar into a list and calls myFunction with the list as argument

(c) Make myFunction intelligent enough to accept either a scalar or a list

I settled for (c), because I prefered this way of usage, and it seems to be also the most interesting one (I'm using the project to learn Python). Therefore, I need to insert a few lines in the function body, which test, whether the parameter is a list or a scalar, and if it is a scalar, turn it into an list. If it is already a list, nothing needs to be done. After this, the function is executed as before, but guaranteed to operate on a list.

Did I explain clearly enough my intent? I can post the actual code of my function if necessary, but I fear that it would just unnecessarily distract from the problem at hand.
rovf
 
Posts: 25
Joined: Fri Aug 16, 2013 4:35 pm

Re: Style question: Disambiguating number from list of numbe

Postby stranac » Wed Dec 11, 2013 10:45 am

Honestly, going with the first option would make more sense.
Just a few reasons I would choose that:
  • it's simpler to implement
  • it's easier to understand when reading
  • it's more explicit
I would even take the second option rather than the third one, for similar reasons.

If you're determined to go with the third option, post more details(preferably with code) about the function itself.
Friendship is magic!

R.I.P. Tracy M. You will be missed.
User avatar
stranac
 
Posts: 1155
Joined: Thu Feb 07, 2013 3:42 pm

Re: Style question: Disambiguating number from list of numbe

Postby rovf » Wed Dec 11, 2013 11:49 am

stranac wrote:Honestly, going with the first option would make more sense.


I can understand your point. I think I'm more inclined to the third option for two reason: It is more interesting to implement (this is not a production project, but one for learning, where I want experiment with various Python features), and because in the past I had programmed for some time in APL, where it is common to have a function being called by a vector and a scalar, and the scalar (depending on the context) is treated of a vector of appropriate length, filled with identical elements.

stranac wrote:If you're determined to go with the third option, post more details(preferably with code) about the function itself.


Sure. Here it is. This is the function (actually a constructor), which for the parameter initial_levels (the 6th parameter) expects the list of 3 elements:

Code: Select all
KANJI = 0
KANA = 1
GAIKOKUJI = 2

# ....

class Goi:
    def __init__(self,_initial_last_asked_event_number,_kanji,_kana,_gaikoku,initial_levels,_kanji_atari=0,_kana_atari=0,_gaikoku_atari=0):
        self.ji=new_repr_list()
        if len(_kanji) > 0:
            self.ji[KANJI]=Kotoba(_kanji,initial_levels[KANJI],_kanji_atari)
        self.ji[KANA]=Kotoba(_kana,initial_levels[KANA],_kana_atari)
        self.ji[GAIKOKUJI]=Kotoba(_gaikoku,initial_levels[GAIKOKUJI],_gaikoku_atari)
        self.most_recently_queried=_initial_last_asked_event_number       


For the enumeration values defined at the beginning (KANJI, KANA, GAIKOKUJI), I would have prefered an automatic enumeration method (similar to what we have in C or Ada), instead of having to supply the values explicitly, but I didn't find such a feature in Python yet.

Indeed, nothing in this code requires, that initial_levels is really a list. It could for example be a dictionary, and I think there are some good reason to do it, but let this aside for a moment, as this is a different issue (refactoring).

Note also that any solution should run on Python 2.3.5.
rovf
 
Posts: 25
Joined: Fri Aug 16, 2013 4:35 pm

Re: Style question: Disambiguating number from list of numbe

Postby stranac » Wed Dec 11, 2013 1:31 pm

I would do it like this:
Code: Select all
def __init__(whatever):
    try:
        kanji_level, kana_level, gaikokuji_level = initial_levels
    except TypeError:
        kanji_level = kana_level = gaikokuji_level = initial_levels
    # continue your code using the new variables

Also, your class should inherit from object.
That is assuming tuple unpacking and new-style classes work in 2.3.5...
Why is it required to run on a super ancient version of python?

Off topic: I've never encountered the word gaikokuji before. Are those like foreign characters?(looks like that, but can't be sure)
Friendship is magic!

R.I.P. Tracy M. You will be missed.
User avatar
stranac
 
Posts: 1155
Joined: Thu Feb 07, 2013 3:42 pm

Re: Style question: Disambiguating number from list of numbe

Postby Mekire » Wed Dec 11, 2013 1:58 pm

stranac wrote:Off topic: I've never encountered the word gaikokuji before. Are those like foreign characters?(looks like that, but can't be sure)

The meaning would certainly be foreign character/letter, but I have never encountered this either. Gaikokugo, yes (as this is allegedly what I teach). I am assuming he means romaji, but who knows. My kids are bad enough with romaji without throwing any other language systems in there.

-Mek
User avatar
Mekire
 
Posts: 988
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Style question: Disambiguating number from list of numbe

Postby micseydel » Wed Dec 11, 2013 6:47 pm

I'm not familiar with APL but the idea of treating a scalar like a vector sometimes sounds domain specific, like in Matlab or matplotlib (the Python module which aims to allow Python to do matplotlib things). For general purpose programming, or if I'm right about what you're doing, game programming, I would avoid it. I really don't see any reason for it. Perhaps if you showed a code example of what you're really trying to do you could convince me, but it sounds like the functions which operator on the different types should likely should have different names, since the caller will probably know what type their object is anyway.
Join the #python-forum IRC channel on irc.freenode.net!

Please do not PM members regarding questions which are meant to be discussed publicly. The point of the forum is so that others can benefit from it. We don't want to help you over PMs or emails.
User avatar
micseydel
 
Posts: 1390
Joined: Tue Feb 12, 2013 2:18 am
Location: Mountain View, CA

Re: Style question: Disambiguating number from list of numbe

Postby rovf » Tue Dec 17, 2013 3:11 pm

stranac wrote:Off topic: I've never encountered the word gaikokuji before. Are those like foreign characters?(looks like that, but can't be sure)


Not surprising, that you haven't heard it before: The term is my invention. I wanted to name a variable, which holds a text, which is written in some writing system different from Kana or Kanji (it could be Romaji, Cyrillic, Thai, ...), so from the Japanese viewpoint, it would be "gaikoku"-Ji.

I'm happy for any suggestion of a better name :-D
rovf
 
Posts: 25
Joined: Fri Aug 16, 2013 4:35 pm

Re: Style question: Disambiguating number from list of numbe

Postby Somelauw » Fri Dec 20, 2013 11:54 pm

Another way is using hasattr:
Code: Select all
if not hasattr(data, "len"):
    data = [data for i in range(3)]
return data[0]
Join the #python-forum IRC channel on irc.freenode.net!
Somelauw
 
Posts: 72
Joined: Tue Feb 12, 2013 8:30 pm


Return to General Discussions

Who is online

Users browsing this forum: No registered users and 2 guests