3

I come from C# and Java, so lose typing is still new to me, it might show.

I have a function which shoots of a query to a a database to remove duplicates which have occurred lately. Say, withing the last 14 days or so. When the script runs daily I'd like it to only lookback using an integer of days like 14; however, sometimes I might want to supply a datetime and have it use it directly instead of calculating what date it would be by subtraction of an integer.

The following is pretty much what I have at the moment:

def clean_up_duplicates(dup_lookback):
    if hasattr(dup_lookback, 'strftime'):
        dup_start_str = dup_lookback.strftime('%Y-%m-%d %H:%M:%S')
    else:
        dup_start = date.today() - timedelta(days=dup_lookback)
        dup_start_str = dup_start.strftime('%Y-%m-%d %H:%M:%S')

    # ... continue on and use dup_start_str in a query.

Is this correct use of ducktyping? Is this pythonic?

My alternative, of the top of my head, is to split up in two functions:

def clean_up_duplicates_since_days(dup_lookback_days):
    dup_start_str = dup_lookback.strftime('%Y-%m-%d %H:%M:%S')
    __clean_up_duplicates(dup_start_str)

def clean_up_duplicates_since_datetime(dup_lookback_datetime):
    dup_start = date.today() - timedelta(days=dup_lookback)
    dup_start_str = dup_start.strftime('%Y-%m-%d %H:%M:%S')
    __clean_up_duplicates(dup_start_str)

def __clean_up_duplicates(dup_start_str):
    # ... continue on and use dup_start_str in a query.

(Coded this last bit in the browser, so might be off)

André C. Andersen
  • 8,955
  • 3
  • 53
  • 79
  • In Python you generally don't use `hasattr`, you just try to call the function and then catch the error that gets generated if it doesn't exist. – Mark Ransom Apr 28 '15 at 22:39
  • I tend to prefer the first version. the second one is using specialized methods making assumptions on the types of arguments, so not very duck typing – fferri Apr 28 '15 at 22:40
  • @MarkRansom Is there a consensus about using hasattr for duck typing? Answers to the following question seems to argue for 'try' if the common case happens often, while hasattr if it is an even split: http://stackoverflow.com/questions/903130/hasattr-vs-try-except-block-to-deal-with-non-existent-attributes – André C. Andersen Apr 28 '15 at 22:45
  • @AndréChristofferAndersen maybe there's not a consensus, but most of the arguments I've seen prefer the try/catch model just for stylistic reasons. P.S. splitting the function up does seem clearer altogether, although you can get by with 2 functions instead of 3. – Mark Ransom Apr 28 '15 at 22:49
  • After seeing your answer, I'd tend to agree. It looks good. – André C. Andersen Apr 28 '15 at 22:51
  • @AndréChristofferAndersen: the thing is that in C# and Java, because of overloading, everyone expects `clean_up_dupes(int)` and `clean_up_dupes(Date)` to treat their input differently in some respect. In Python, emulating that overloading makes your function somewhat astonishing, never mind how you implement the check. – Steve Jessop Apr 28 '15 at 23:47
  • @SteveJessop Thanks for your input. Astonishing to the C#/Java dev., or the python dev.? Are you saying I am misusing/abusing the flexibility of python? I hope not. I'm a little confused. – André C. Andersen May 01 '15 at 14:13
  • @AndréChristofferAndersen: Astonishing to the Python dev. I don't think you're abusing Python, but it's more common in Python to use keyword arguments for functions that can work with alternative different inputs of different types that have different meanings. Or just write two functions, but you already considered and rejected that ;-) – Steve Jessop May 01 '15 at 14:15
  • @SteveJessop No no, I didn't reject using two functions at all. It was just an after thought. It might very well be the right way to go. I'm trying to make my code as pythonic as possible so that future python developers will have a nice time debugging my old stuff. – André C. Andersen May 01 '15 at 16:54

1 Answers1

1

Duck typing means you assume that an object contains a function, and if it does then everything just works.

If there's an alternate path to take if the function doesn't exist, catch it with a try/except.

def clean_up_duplicates(dup_lookback):
    try:
        dup_start_str = dup_lookback.strftime('%Y-%m-%d %H:%M:%S')
    except AttributeError:
        dup_start = date.today() - timedelta(days=dup_lookback)
        dup_start_str = dup_start.strftime('%Y-%m-%d %H:%M:%S')
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Thanks Mark. This looks good, also my specific problem has a common case of using `dup_lookback` as an integer so it makes sense to lead with it, opposite of what you have here, though still very applicable. – André C. Andersen Apr 28 '15 at 22:50