pythonic way to convert variable to list

前端 未结 8 493
时光说笑
时光说笑 2020-12-24 03:14

I have a function whose input argument can either be an element or a list of elements. If this argument is a single element then I put it in a list so I can iterate over the

相关标签:
8条回答
  • 2020-12-24 03:34

    Typically, strings (plain and unicode) are the only iterables that you want to nevertheless consider as "single elements" -- the basestring builtin exists SPECIFICALLY to let you test for either kind of strings with isinstance, so it's very UN-grotty for that special case;-).

    So my suggested approach for the most general case is:

      if isinstance(input, basestring): input = [input]
      else:
        try: iter(input)
        except TypeError: input = [input]
        else: input = list(input)
    

    This is THE way to treat EVERY iterable EXCEPT strings as a list directly, strings and numbers and other non-iterables as scalars (to be normalized into single-item lists).

    I'm explicitly making a list out of every kind of iterable so you KNOW you can further on perform EVERY kind of list trick - sorting, iterating more than once, adding or removing items to facilitate iteration, etc, all without altering the ACTUAL input list (if list indeed it was;-). If all you need is a single plain for loop then that last step is unnecessary (and indeed unhelpful if e.g. input is a huge open file) and I'd suggest an auxiliary generator instead:

    def justLoopOn(input):
      if isinstance(input, basestring):
        yield input
      else:
        try:
          for item in input:
            yield item
        except TypeError:
          yield input
    

    now in every single one of your functions needing such argument normalization, you just use:

     for item in justLoopOn(input):
    

    You can use an auxiliary normalizing-function even in the other case (where you need a real list for further nefarious purposes); actually, in such (rarer) cases, you can just do:

     thelistforme = list(justLoopOn(input))
    

    so that the (inevitably) somewhat-hairy normalization logic is just in ONE place, just as it should be!-)

    0 讨论(0)
  • 2020-12-24 03:34

    That is an ok way to do it (don't forget to include tuples).

    However, you may also want to consider if the argument has a __iter__ method or __getitem__ method. (note that strings have __getitem__ instead of __iter__.)

    hasattr(arg, '__iter__') or hasattr(arg, '__getitem__')
    

    This is probably the most general requirement for a list-like type than only checking the type.

    0 讨论(0)
  • 2020-12-24 03:40

    You can put * before your argument, this way you'll always get a tuple:

    def a(*p):
      print type(p)
      print p
    
    a(4)
    >>> <type 'tuple'>
    >>> (4,)
    
    a(4, 5)
    >>> <type 'tuple'>
    >>> (4,5,)
    

    But that will force you to call your function with variable parameters, I don't know if that 's acceptable for you.

    0 讨论(0)
  • 2020-12-24 03:46

    You can do direct type comparisons using type().

    def my_func(input):
        if not type(input) is list:
            input = [input]
        for e in input:
            # do something
    

    However, the way you have it will allow any type derived from the list type to be passed through. Thus preventing the any derived types from accidentally being wrapped.

    0 讨论(0)
  • 2020-12-24 03:50

    First, there is no general method that could tell a "single element" from "list of elements" since by definition list can be an element of another list.

    I would say you need to define what kinds of data you might have, so that you might have:

    • any descendant of list against anything else
      • Test with isinstance(input, list) (so your example is correct)
    • any sequence type except strings (basestring in Python 2.x, str in Python 3.x)
      • Use sequence metaclass: isinstance(myvar, collections.Sequence) and not isinstance(myvar, str)
    • some sequence type against known cases, like int, str, MyClass
      • Test with isinstance(input, (int, str, MyClass))
    • any iterable except strings:
      • Test with

    .

        try: 
            input = iter(input) if not isinstance(input, str) else [input]
        except TypeError:
            input = [input]
    
    0 讨论(0)
  • 2020-12-24 03:51

    Your aproach seems right to me.

    It's similar to how you use atom? in Lisp when you iterate over lists and check the current item to see if it is a list or not, because if it is a list you want to process its items, too.

    So, yeah, don't see anything wrong with that.

    0 讨论(0)
提交回复
热议问题