Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UserList overloading python 3.x

I'm trying to overload UserList methods (add, append, extend) to get no duplicates. For some reason, I still get alpha twice. I even get an empty list if I just try to print the original_list Here's the code I wrote:

#!/usr/bin/env python
#coding: utf-8

from collections import UserList

class Ulist(UserList):
    def __init__(self, info = []):
        UserList.__init__(self)
        self.info = info

    def __add__(self, something_new):
        for i in something_new:
            if i in self:
                print('%r This is already in the list.' % (i))
            else:
                return UserList.__add__(self, something_new)

    def append(self, something_new):
        if something_new in self:
            print('%r This is already in the list.' % (i))
        else:
            return UserList.append(self, something_new)

    def extend(self, something_new):
        for i in something_new:
            if i in self:
                print('%r This is already in the list.' % (i))
            else:
                return UserList.extend(self, something_new)


# let's test how it works

original_list = Ulist(['alpha'])

original_list.__add__([444])
original_list.append('alpha')
original_list.append('I_dont_exist_yet')
original_list.append(0)
original_list.extend([98, 98, 234,'alpha','beta','I_am_totally_new',33])


print(original_list)

Disclaimer: I'm aware people have asked a similar question regarding UserList and method overloading. I analysed these questions but still couldn't figure out why my code doesn't work. Also, I'm new to Python.

like image 535
Maja Gwóźdź Avatar asked Dec 19 '25 00:12

Maja Gwóźdź


2 Answers

Are you trying to do something like this?

from collections import UserList

class Ulist(UserList):
    def __init__(self, info = []):
        UserList.__init__(self)
        self.info = info

    def add_iterable(self, iterable):
        for i in iterable:
            if i in self:
                print('%r This is already in the list.' % (i))
            else:
                UserList.append(self, i)
        return self

    def __add__(self, something_new):
        if hasattr(something_new, '__iter__'):
            return self.add_iterable(something_new)
        else:
            return UserList.append(self, something_new)

    def append(self, something_new):
        if something_new in self:
            print('%r This is already in the list.' % (i))
        else:
            return UserList.append(self, something_new)

    def extend(self, something_new):        
        return self.add_iterable(something_new)

Test output:

98 This is already in the list.

'alpha' This is already in the list.

[444, 'alpha', 'I_dont_exist_yet', 0, 98, 234, 'beta', 'I_am_totally_new', 33]

like image 147
Yohanes Gultom Avatar answered Dec 21 '25 00:12

Yohanes Gultom


This is only tangentially an answer, but I'd strongly recommend trying to implement similar operations in terms of each other. For example, __add__ is really just construction followed by an extend operation. And extend is really just repeated appends. If performance is not absolutely critical (and when you're implementing your own UserList, you've already given up on performance, by and large), it's much easier to have one known good implementation and implement other operations in terms of that known good operation.

So, for example, __add__ can be implemented as:

def __add__(self, other):
    ret = self.__class__(self)  # Alt: self.copy()
    ret.extend(other)
    return ret

So now as long as your constructor and extend method are correct, __add__ is correct automatically.

Then you implement extend in terms of append:

def extend(self, other):
    for x in other:
        self.append(x)

and now, if append is correct, then so are extend and __add__. Now, all you need to do is get append correct (and you can test it in isolation until you're sure it's correct).

As it happens, your append is already correct (assuming print is a reasonable way of reporting errors, it usually isn't). So with this __add__ and extend, and a working initializer method, you'd have working code.

Your existing code would not actually behave correctly regardless though. Your initializer is wrong; you set self.info to the input iterable but self.info is never used by collections.UserList (it uses the data attribute). If you want to make a safe initializer, do something like:

def __init__(self, info=()):  # Never use mutable default arguments
    UserList.__init__(self)
    self.extend(info)  # Reuse known good code

Again, this reuses known good code; if extend works (which in turn depends on append), then this initializer is good. If you want it optimized for the copy scenario, you can save duplicate checking work by using class internals:

def __init__(self, info=()):
    UserList.__init__(self)
    if isinstance(info, Ulist):
        # Already deduped, copy without duplicate checking
        self.data[:] = info.data
    else:
        self.extend(info)

The main point of this answer is: Build up from pieces, and don't repeat yourself (mnemonic: DRY). Don't try to implement each method from scratch when they're all variations on one another. Identify a single "common denominator" functionality, and build on top of that, so any given method either does a single simple thing, or is implemented in terms of another method in a way that minimizes custom code.

like image 23
ShadowRanger Avatar answered Dec 21 '25 00:12

ShadowRanger