0

I have a utility method for printing lists of items, which also handles a few other things (like removing newline characters, etc.). I would like to expand my function so I can also pass it strings, and it will print the string the same way it would a list. Currently, it will iterate and print each line of the string separately. Here is an example:

#The function
def print_content(content):
#do some modification of the list
    for i in content:
        print(i)
#if I pass in a list, I will get output like:
>Item 1
>Item 2
>Item 3
#if I pass in a string, I will get output like:
>I
>t
>e
>m
>
>1
#But I would like it to read:
>Item 1

So my idea was to check if the variable was a string, and it it was, turn it into a list. However, if the variable is a list, leave it alone. How might I accomplish this or what other ways might I achieve this task?

perennial_
  • 1,798
  • 2
  • 26
  • 41

2 Answers2

0

Though martineau's answer is overkill in my opinion, he makes a solid point about avoiding type checking. I would suggest to define a class for which you are explicit about how you wish to maintain your list.

#!/usr/bin/python

class Content(object):
    def __init__(self):
        self.content_list = []

    def add_string_to_content(self, a_string):
        self.content_list.append(a_string)

    def print_content(self):
        for i in self.content_list:
            print(i)

    def append_list_to_content(self, a_list):
        self.content_list += a_list

def main():
    content_list = ['item 1', 'item 2']
    content_string = "item 3"
    some_content = Content()
    some_content.append_list_to_content(content_list)
    some_content.add_string_to_content(content_string)
    some_content.print_content()

if __name__ == "__main__":
    main()

The output of which is:

brock@brock-desktop:~/testing$ python test.py 
item 1
item 2
item 3

You could even write a small parser method that would take a string "item 1 item 2" and turn it into ["item 1", "item 2"]. The point is that method would be contained in the class and explicitly named to avoid any confusion

Brock Hargreaves
  • 842
  • 6
  • 12
-1

Rather than checking for specific type(s), which many feel is very "unpythonic", here's a general way to do it that uses a metaclass based on abstract base classes. This allows you to selectively define which types you want considered atomic (non-iterable) by just registering them, regardless of whether they are, in a strict Python sense, or not.

As shown, it can be applied to existing classes and also works in both Python 2.6+ & 3.x. It may seem like a lot of code, but it can be put into a standalone module for easy reuse.

Based on an answer to the question Correct way to detect sequence parameter?

import abc  # requires Py 2.6+
import collections

class _AtomicBase(object):
    """ Metaclass of types to be considered indivisible rather than iterable.
            usage:  isinstance(<obj>, Atomic)
    """
    @classmethod
    def __subclasshook__(cls, other):
        return not issubclass(other, collections.Sequence) or NotImplemented

class Atomic(abc.ABCMeta("NewMeta", (_AtomicBase,), {})):
    pass

# use the abstract base class to make strings be considered atomic
try:
    # basestring is the abstract superclass of both str and unicode in Py 2.
    Atomic.register(basestring)  # Will make both types considered Atomic.
except NameError:  # 'basestring' is undefined, assume Python >= 3
    Atomic.register(str)  # str includes unicode in Py 3, make both Atomic
    Atomic.register(bytes)  # bytes should also be considered Atomic

if __name__ == '__main__':
    print('Is [1,2,3] Atomic? -> {}'.format(isinstance([1,2,3], Atomic)))  # -> False
    print('Is "abc" Atomic? -> {}'.format(isinstance("abc", Atomic)))  # -> True
martineau
  • 119,623
  • 25
  • 170
  • 301