Duke Dougal
Duke Dougal

Reputation: 26406

Python ducktyping feels clunky is this Pythonic?

Python 3

Data might be a string, bytes or an EmailMessage.

Is this the most effective way to get the bytes out of data?

    while True:
        try:
            # data is a string
            f.write(data.encode('utf-8'))
            break
        except:
            pass
        try:
            # data is an EmailMessage
            f.write(data.get_bytes())
            break
        except:
            pass
        try:
            # data is bytes
            f.write(data)
            break
        except:
            pass
        else:
            self.log.info('Exiting, unknown attachment type')
            sys.exit(1)

Upvotes: 2

Views: 233

Answers (2)

Martin Konecny
Martin Konecny

Reputation: 59681

From wikipedia:

For example, in a non-duck-typed language, one would create a function that requires that the object passed into it be of type Duck, in order to ensure that that function can then use the object's walk and quack methods. In a duck-typed language, the function would take an object of any type and simply call its walk and quack methods, producing a run-time error if they are not defined. Instead of specifying types formally, duck typing practices rely on documentation, clear code, and testing to ensure correct use.

This doesn't mean you can call whatever methods you feel like, and catch an exception if it doesn't exist. You still need to make some effort to only call methods you are sure will be there. Duck-typing is more related to the fact that you don't care what the type of the object is (if you passed it into a function, you don't specify the type as you would in Java), but are still sure that it will respond to a specific method call.

In your case, what I would do is create a wrapper around each of those objects (String, EmailMessage, bytes), with a common method get_data, where each implementation of get_data is specific to the type of object it wraps around (encode, get_bytes etc.). Then your loop would look as follows:

while True:
    try:
        # data is a string
        f.write(data.get_data())
        break
    except:
        pass

Edit: You may also want to see this question which is related to yours: How to handle "duck typing" in Python?

Upvotes: 1

Hugh Bothwell
Hugh Bothwell

Reputation: 56684

if hasattr(data, "encode"):
    f.write(data.encode('utf-8'))
elif hasattr(data, "get_bytes"):
    f.write(data.get_bytes())
else:
    try:
        f.write(data)
    except TypeError:
        self.log.info('Exiting, unknown attachment type')
        sys.exit(1)

Edit:

This is ducktyping - as in "if it quacks like a duck, it is a duck; if it encodes, it is a string".

This is preferred over if isinstance(data, str) because it is less restrictive; so long as an object knows how to encode itself to bytes, we don't really care if it is actually a string or not.

Exceptions are relatively slow and should be reserved for handling unexpected or unlikely errors.

Upvotes: 7

Related Questions