You only have to change your super()
call to use explicit arguments:
super(Palette, self).__init__(*args)
and your code will work just fine in both Python 2 and Python 3. See Why is Python 3.x's super() magic? for background information on why the above is equivalent to super().__init__(*args)
. Also, do not pass in self
again, or you'll create a circular reference as you include self
in the contents of the list.
Note that it is more pythonic use property
objects instead of explicit getters and setters:
class Palette(list):
def __init__(self, name=None, description=None, colors=None, *args):
super(Palette, self).__init__(args)
self.name = name
self.description = description
self.extend(colors)
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
@name.deleter
def name(self):
self.name = None
@property
def description(self):
return self._description
@description.setter
def description(self, description):
self._description = description
@description.deleter
def description(self):
self.description = None
then use
palette1.description = "This is palette 1."
I also took the liberty of reducing the amount of whitespace in the function definitions; putting each and every argument on a new line makes it very hard to get an overview of the class as function bodies are needlessly pushed down.
As these properties don't actually do anything other than wrap an attribute by the same name with an underscore, you may as well just leave them out altogether. Unlike Java, in Python you can freely switch between using attributes directly, and later on swapping attributes out for a property object; you are not tied into one or the other.
Note that in both Python 2 and Python 3, you cannot pass in positional arguments; the following doesn't work:
Palette('#F1E1BD', '#EEBA85', name='palette2')
because the first positional argument will be assigned to the name
argument:
>>> Palette('#F1E1BD', '#EEBA85', name='palette2')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() got multiple values for argument 'name'
To support that use case, you need to not name the keyword arguments in the signature, and only use **kwargs
, then retrieve your keyword arguments from that. Pass in any positional arguments as one argument so that list()
takes any number of positional arguments as the contents for the new list:
class Palette(list):
def __init__(self, *args, **kwargs):
super(Palette, self).__init__(args)
self.name = kwargs.pop('name', None)
self.description = kwargs.pop('description', None)
self.extend(kwargs.pop('colors', []))
if kwargs:
raise TypeError('{} does not take {} as argument(s)'.format(
type(self).__name__, ', '.join(kwargs)))
Demo:
>>> class Palette(list):
... def __init__(self, *args, **kwargs):
... super(Palette, self).__init__(args)
... self.name = kwargs.pop('name', None)
... self.description = kwargs.pop('description', None)
... self.extend(kwargs.pop('colors', []))
... if kwargs:
... raise TypeError('{} does not take {} as argument(s)'.format(
... type(self).__name__, ', '.join(kwargs)))
...
>>> palette1 = Palette(
... name = "palette 1",
... colors = [
... "#F1E1BD",
... "#EEBA85",
... "#E18D76",
... "#9C837E",
... "#5B7887"
... ]
... )
>>> palette2 = Palette("#F1E1BD", "#EEBA85", "#E18D76", "#9C837E", "#5B7887",
... name="palette 2")
>>> palette1
['#F1E1BD', '#EEBA85', '#E18D76', '#9C837E', '#5B7887']
>>> palette2
['#F1E1BD', '#EEBA85', '#E18D76', '#9C837E', '#5B7887']
>>> palette1.name
'palette 1'
>>> palette2.name
'palette 2'
>>> palette1.description = 'This is palette 1.'
>>> palette2.description = 'This is palette 2.'
>>> Palette(foo='bar', spam='eggs')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in __init__
TypeError: Palette does not take foo, spam as argument(s)