Extending bp's answer, I wanted to show you what he meant by immutable types.
First, this is okay:
>>> class TestB():
... def __init__(self, attr=1):
... self.attr = attr
...
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1
However, this only works for immutable (unchangable) types. If the default value was mutable (meaning it can be replaced), this would happen instead:
>>> class Test():
... def __init__(self, attr=[]):
... self.attr = attr
...
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>>
Note that both a and b have a shared attribute. This is often unwanted.
This is the Pythonic way of defining default values for instance variables, when the type is mutable:
>>> class TestC():
... def __init__(self, attr=None):
... if attr is None:
... attr = []
... self.attr = attr
...
>>> a = TestC()
>>> b = TestC()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[]
The reason my first snippet of code works is because, with immutable types, Python creates a new instance of it whenever you want one. If you needed to add 1 to 1, Python makes a new 2 for you, because the old 1 cannot be changed. The reason is mostly for hashing, I believe.
Answer from Xavier Ho on Stack OverflowExtending bp's answer, I wanted to show you what he meant by immutable types.
First, this is okay:
>>> class TestB():
... def __init__(self, attr=1):
... self.attr = attr
...
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1
However, this only works for immutable (unchangable) types. If the default value was mutable (meaning it can be replaced), this would happen instead:
>>> class Test():
... def __init__(self, attr=[]):
... self.attr = attr
...
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>>
Note that both a and b have a shared attribute. This is often unwanted.
This is the Pythonic way of defining default values for instance variables, when the type is mutable:
>>> class TestC():
... def __init__(self, attr=None):
... if attr is None:
... attr = []
... self.attr = attr
...
>>> a = TestC()
>>> b = TestC()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[]
The reason my first snippet of code works is because, with immutable types, Python creates a new instance of it whenever you want one. If you needed to add 1 to 1, Python makes a new 2 for you, because the old 1 cannot be changed. The reason is mostly for hashing, I believe.
The two snippets do different things, so it's not a matter of taste but a matter of what's the right behaviour in your context. Python documentation explains the difference, but here are some examples:
Exhibit A
class Foo:
def __init__(self):
self.num = 1
This binds num to the Foo instances. Change to this field is not propagated to other instances.
Thus:
>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1
Exhibit B
class Bar:
num = 1
This binds num to the Bar class. Changes are propagated!
>>> bar1 = Bar()
>>> bar2 = Bar()
>>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation
>>> bar2.num
1
>>> Bar.num = 3
>>> bar2.num
3
>>> bar1.num
2
>>> bar1.__class__.num
3
Actual answer
If I do not require a class variable, but only need to set a default value for my instance variables, are both methods equally good? Or one of them more 'pythonic' than the other?
The code in exhibit B is plain wrong for this: why would you want to bind a class attribute (default value on instance creation) to the single instance?
The code in exhibit A is okay.
If you want to give defaults for instance variables in your constructor I would however do this:
class Foo:
def __init__(self, num = None):
self.num = num if num is not None else 1
...or even:
class Foo:
DEFAULT_NUM = 1
def __init__(self, num = None):
self.num = num if num is not None else DEFAULT_NUM
...or even: (preferrable, but if and only if you are dealing with immutable types!)
class Foo:
def __init__(self, num = 1):
self.num = num
This way you can do:
foo1 = Foo(4)
foo2 = Foo() #use default
I'm wondering if this is the right way to do this. It seems... odd somehow to assign default values in the class definition, then default None in the constructor. I suppose I could give the same default values in both places, but again, this feels like unnecessarily repeating myself.
class MyClass:
name = 'Undefined'
number = -1
def __init__(self, name=None, number=None):
if name is not None:
self.name = name
if number is not None:
self.number = numberDon't use class attributes for that, that's not what they are there for. Use default arguments:
def __init__(self, name='Undefined', number=-1):
self.name = name
self.number = number
If any of the arguments are mutable, then you need to use the usual idiom:
def __init__(self, foo=None):
self.foo = [] if foo is None else foo
do you need these vars on the class level, given that they are supposed to be relevant on a per instance basis?
class MyClass:
def __init__(self, name='Undefined', number=-1):
self.name = name
self.number = number
def __str__(self):
return '{0.__class__.__name__}(name={0.name}, number={0.number})'.format(self)
print(MyClass())
print(MyClass(name='Joe'))
print(MyClass(number=11))
print(MyClass(name='Joe', number=11))
python - Setting default values in a class - Stack Overflow
A way to have the default value for a parameter as the default of another function: the Default singleton - Ideas - Discussions on Python.org
Understand the behavior of default params in python classes
python - How to assign "default" values in class? - Stack Overflow
There is a whole bunch of ways to solve this problem, but if you have python 3.7 installed (or have 3.6 and install the backport), dataclasses might be a good fit for a nice solution.
First of all, it lets you define the default values in a readable and compact manner, and also allows all the mutation operations you need:
>>> from dataclasses import dataclass
>>> @dataclass
... class Plots:
... a: int = 1
... b: int = 2
... c: int = 3
...
>>> p = Plots() # create a Plot with only default values
>>> p
Plots(a=1, b=2, c=3)
>>> p.a = -1 # update something in this Plot instance
>>> p
Plots(a=-1, b=2, c=3)
You also get the option to define default factories instead of default values for free with the dataclass field definition. It might not be a problem yet, but it avoids the mutable default value gotcha, which every python programmer runs into sooner or later.
Last but not least, writing a reset function is quite easy given an existing dataclass, because it keeps track of all the default values already in its __dataclass_fields__ attribute:
>>> from dataclasses import dataclass, MISSING
>>> @dataclass
... class Plots:
... a: int = 1
... b: int = 2
... c: int = 3
...
... def reset(self):
... for name, field in self.__dataclass_fields__.items():
... if field.default != MISSING:
... setattr(self, name, field.default)
... else:
... setattr(self, name, field.default_factory())
...
>>> p = Plots(a=-1) # create a Plot with some non-default values
>>> p
Plots(a=-1, b=2, c=3)
>>> p.reset() # calling reset on it restores the pre-defined defaults
>>> p
Plots(a=1, b=2, c=3)
So now you can write some function do_stuff(...) that updates the fields in a Plot instance, and as long as you execute reset() the changes won't persist.
You can use class variables, and property to achieve your goal to set default values for all class instances. The instances values can be modified directly, and the initial default values restored after calling a method.
In view of the context that "the real class has dozens of default values", another approach that you may consider, is to set up a configuration file containing the default values, and using this file to initialize, or reset the defaults.
Here is a short example of the first approach using one class variable:
class Plots:
_a = 1
def __init__(self):
self._a = None
self.reset_default_values()
def reset_default_values(self):
self._a = Plots._a
@property
def a(self):
return self._a
@a.setter
def a(self, value):
self._a = value
plot = Plots()
print(plot.a)
plot.a = 42
print(plot.a)
plot.reset_default_values()
print(plot.a)
output:
1
42
1
I am learning linked lists in python using classes.
One place that I am learning from is doing:
class Node:
def __init__(self, value):
self.value = value
self.next = NoneThe other place is doing:
class Node:
def __init__(self, value, next = None):
self.value = value
self.next = nextI can't figure out what difference it makes to use either init method. I tried to search this answer out using StackOverflow, but I had no luck.
I get that one is able to define 'next' during instantiation and the other would need to be defined after instantiation, but when would and why would you use one over the other?
edit: Thanks all. I am just starting to understand classes and still overthinking a little.
Your understanding is wrong. self is itself a parameter to that function definition, so there's no way it can be in scope at that point. It's only in scope within the function itself.
The answer is simply to default the argument to None, and then check for that inside the method:
def doSomething(self, a=None):
if a is None:
a = self.z
self.z = 3
self.b = a
Default arguments get evaluated only once, when the definition is executed. Instead, do this:
def doSomething(self, a=None):
if a is None:
a = self.z
self.z = 3
self.b = a
See also http://docs.python.org/release/3.3.0/tutorial/controlflow.html#more-on-defining-functions.
Some minor comments on the code:
- Class definition should be separated from the line with import by two spaces.
My personal preference is to have key-value pairs in dictionaries separated with a space after colon as shown in PEP 8:
defaults = {'A': None, 'B': 0, 'C': 0}Comparing to
Noneshould be done byisinstead of==:required = [key for key, value in defaults.items() if value is None]Note that I also removed redundant brackets around
key, value. There are several other lines where brackets are not needed around them.PEP 8 also discourages aligning several lines with assignments by
=, so instead of, for example:results = None keys, values = zip(*setup.items())it should be
results = None keys, values = zip(*setup.items())There is no need to specify
objectinclass O(object),class Owill work fine.Here:
for key, value in kwargs.items(): if key in setup: setup[key] = kwargs[key] # user specified overrides defaultyou don't use
value, but you could:for key, value in kwargs.items(): if key in setup: setup[key] = valueHere:
keys, values = zip(*setup.items())you don't need
valuesas you overwrite them later. So, I'd just remove this line altogether.set((1,))can be replaced with{1}, andset.differencecan be replaced with just-. BTW, I like how you combined two conditions from my previous review in one!Don't forget to use
np.can_castinstead of checking the dtypes againstnp.int64. The current version failed for me until I changed it.[array.astype(float) for array in arrays]can be written aslist(map(np.float64, arrays))but both versions are fine.The overall design looks quite unusual to me. If it would be me, I'd separate validating data from the container that will keep it. In other words, I'd not keep it in one class. BTW, if a class has just two methods and one of them is
__init__then it shouldn't be a class. Another thing you could try is pydantic library. Never had a chance to try it myself though, but with this problem of data validation I'd give it a shot.
If there are required parameters, you should state them explicitly.
class O:
def __init__(self, A=None, B=0, C=0, **kwargs):
By all means I would advise stronly against your solution. Class members should be clearly readable.