python why is a setter preferred? - python

The property decorator is a great way to "protect" attributes one wants to set once and never change again. I usually deal with this this way (btw., following a the advice here):
self._name = 'foo'
#property
def name(self):
return self._name
so trying to set name directly yields an AttributeError.
However, I often see the following pattern:
#name.setter
def name(self, value):
self._name = value
#property
def name(self):
return self._name
which seems a little counter-intuitive, as it enables exactly what I want to avoid, and requires extra coding, i.e, theoretically
self.name = 'bar'
would suffice, although it is clear that this would be the worst way to deal with the problem.
The best explanation I can come up with is something like a message from the author saying "you should not change this attribute but if you really want to, there is a mechanism to do it without changing a 'protected' attribute". But then, python doesn't really protect attributes.
So, what's the point, which is more pythonic and why?

You're correct that there's no good reason to use a property if you're not doing anything special in the getter or setter. However, if you do want to do something special (like validate new values, or normalize them in some way), then it makes a lot of sense.
For example, this class's foo attribute will always be clamped between 0 and 1 (and non-numerical values will cause an error immediately):
class Foo:
_foo = 1.0
#foo
def probability(self):
return self._foo
#foo.setter
def foo(self, value):
if value < 0:
value = 0
elif value > 1:
value = 1
self._foo = value
An example with a trivial setter, but a complicated getter might be something like this (deferring an expensive initialization that might not be needed):
class Foo:
_foo = None
def initialize_foo(self):
self._foo = some_expensive_calculation()
#property
def foo(self):
if self._foo is None:
self.initialize_foo() # need the default value
return self._foo
#foo.setter
def foo(self, value):
self._foo = value

If the setter and getter are just directly writing and reading the protected variable, then they're pointless, and using it is not Pythonic; it's just wasting time on property overhead for each access. The attribute should just be made public and the property removed.
The advantage to properties is when you need to replace that simple public attribute with something more powerful, because you can make a property that continues to act like it should for code that was using the attribute, but performs additional work as well. Unless you have additional work though, stick with the attribute if you'd allow it to be written anyway.
Note: Technically, a getter and a setter isn't 100% equivalent to the attribute, since without a deleter, it's not behaviorally identical to the raw attribute. But enforcing non-deletability in your API is silly; developers who go around calling del obj.attr on random attributes of third-party class instances (or almost any instance really) deserve what's coming to them, and you shouldn't be defending against that nonsense at the expense of slowing down and complicating normal use patterns.

Related

How to keep python project modular?

Context
I've been working on a python project recently, and found modularity very important. For example you made a class with some attributes and some line of code that uses those attributes like
a = A()
print("hi"+a.imA)
If you were to modify imA of class A to another type, you would have to modify the print statement. In my case I had to do this so many times. It was annoying and time consuming. get/set methods would've solved this, but I heard that get/set are not 'good python'. So how would you solve this problem without using get and set methods?
First point: you would have saved yourself quite some hassle by using string formatting instead of string concatenation, ie:
print("hi {}".format(a.imA))
Granted, the final result may or not be what you'd expect depending on how a.imA type implements __str__() and __repr__() but at least this will not break the code.
wrt/ getters and setters, they are indeed considered rather unpythonic, because python has a strong support for computed attributes, and a simple generic implementation is available as the builtin property type.
NB: actually what's considered unpythonic is to systematically use implementation attributes and getters/setters (either explicits or - as is the case with computed attributes - implicits) when a plain public attribute is enough, and this is considered unpythonic because you can always turn a plain attribute into a computed one without breaking the client code (assuming of course you don't change the type nor semantic of the attribute) - something that was not possible with early OOPLs like Smalltalk, C++ or Java (Smalltalk being a bit of a special case actually but that's another topic).
In your case, if the point was to change the stored value's type without breaking the API, the simple obvious canonical solution was to use a property delegating to an implementation attribute:
before:
class Foo(object):
def __init__(self, bar):
# `bar` is expected to be the string representation of an int.
self.bar = bar
def frobnicate(self, val):
return (int(self.bar) + val) / 2
after:
class Foo(object):
def __init__(self, bar):
# `bar` is expected to be the string representation of an int.
self.bar = bar
# but we want to store it as an int
#property
def bar(self):
return str(self._bar)
#bar.setter
def bar(self, value):
self._bar = int(value)
def frobnicate(self, val):
# internally we use the implementation attribute `_bar`
return (self._bar + val) / 2
And you now have the value stored internally as an int, but the public interface is (almost) exactly the same - the only difference being that passing something that cannot be passed to int() will raise at the expected place (when you set it) instead than breaking at the most unexpected one (when you call .frobnicate())
Now note that that changing the type of a public attribute is just like changing the return type of a getter (or the type of a setter argument) - in both cases you are breaking the contract - so if what you wanted was really to change the type of A.imA, neither getters nor properties would have solved your issue - getters and setters (or in Python computed attributes) can only protect you from implementation changes.
EDIT: oh and yes: this has nothing to do with modularity (which is about writing decoupled, self-contained code that's easier to read, test, maintain and eventually reuse), but with encapsulation (which aim is to make the public interface resilient to implementation changes).
First, use
print(f"hi {a.imA}") # Python 3.6+
or
print("hi {}".format(a.imA)) # all Python 3
instead of
print("hi"+a.imA)
That way, str will be called automatically on each argument.
Then define a __str__ function in all your classes, so that printing any class always works.
class A:
def __init__(self):
self._member_1 = "spam"
def __str__(self):
return f"A(member 1: {self._member_1})"

How to create a static class property that returns an instance of the class itself?

I wrote a class that can handle integers with arbitrary precision (just for learning purposes). The class takes a string representation of an integer and converts it into an instance of BigInt for further calculations.
Often times you need the numbers Zero and One, so I thought it would be helpfull if the class could return these. I tried the following:
class BigInt():
zero = BigInt("0")
def __init__(self, value):
####yada-yada####
This doesn't work. Error: "name 'BigInt' is not defined"
Then I tried the following:
class BigInt():
__zero = None
#staticmethod
def zero():
if BigInt.__zero is None:
BigInt.__zero = BigInt('0')
return BigInt.__zero
def __init__(self, value):
####yada-yada####
This actually works very well. What I don't like is that zero is a method (and thus has to be called with BigInt.zero()) which is counterintuitive since it should just refer to a fixed value.
So I tried changing zero to become a property, but then writing BigInt.zero returns an instance of the class property instead of BigInt because of the decorator used. That instance cannot be used for calculations because of the wrong type.
Is there a way around this issue?
A static property...? We call a static property an "attribute". This is not Java, Python is a dynamically typed language and such a construct would be really overcomplicating matters.
Just do this, setting a class attribute:
class BigInt:
def __init__(self, value):
...
BigInt.zero = BigInt("0")
If you want it to be entirely defined within the class, do it using a decorator (but be aware it's just a more fancy way of writing the same thing).
def add_zero(cls):
cls.zero = cls("0")
return cls
#add_zero
class BigInt:
...
The question is contradictory: static and property don't go together in this way. Static attributes in Python are simply ones that are only assigned once, and the language itself includes a very large number of these. (Most strings are interred, all integers < a certain value are pre-constructed, etc. E.g. the string module.). Easiest approach is to statically assign the attributes after construction as wim illustrates:
class Foo:
...
Foo.first = Foo()
...
Or, as he further suggested, using a class decorator to perform the assignments, which is functionally the same as the above. A decorator is, effectively, a function that is given the "decorated" function as an argument, and must return a function to effectively replace the original one. This may be the original function, say, modified with some annotations, or may be an entirely different function. The original (decorated) function may or may not be called as appropriate for the decorator.
def preload(**values):
def inner(cls):
for k, v in values.items():
setattr(cls, k, cls(v))
return cls
return inner
This can then be used dynamically:
#preload(zero=0, one=1)
class Foo:
...
If the purpose is to save some time on common integer values, a defaultdict mapping integers to constructed BigInts could be useful as a form of caching and streamlined construction / singleton storage. (E.g. BigInt.numbers[27])
However, the problem of utilizing #property at the class level intrigued me, so I did some digging. It is entirely possible to make use of "descriptor protocol objects" (which the #property decorator returns) at the class level if you punt the attribute up the object model hierarchy, to the metaclass.
class Foo(type):
#property
def bar(cls):
print("I'm a", cls)
return 27
class Bar(metaclass=Foo):
...
>>> Bar.bar
I'm a <class '__main__.Bar'>
<<< 27
Notably, this attribute is not accessible from instances:
>>> Bar().bar
AttributeError: 'Bar' object has no attribute 'bar'
Hope this helps!

Creating a python class with ONLY read-only instance attributes

When developing code for test automation, I often transform responses from the SUT from XML / JSON / whatever to a Python object model to make working with it afterwards easier.
Since the client should not alter the information stored in the object model, it would make sense to have all instance attributes read-only.
For simple cases, this can be achieved by using a namedtuple from the collections module. But in most cases, a simple namedtuple won't do.
I know that the probably most pythonic way would be to use properties:
class MyReadOnlyClass(object):
def __init__(self, a):
self.__a = a
#property
def a(self):
return self.__a
This is OK if I'm dealing only with a few attributes, but it gets lengthy pretty soon.
So I was wondering if there would be any other acceptable approach? What I came up with was this:
MODE_RO = "ro"
MODE_RW = "rw"
class ReadOnlyBaseClass(object):
__mode = MODE_RW
def __init__(self):
self.__mode = MODE_RO
def __setattr__(self, key, value):
if self.__mode != MODE_RW:
raise AttributeError("May not set attribute")
else:
self.__dict__[key] = value
I could then subclass it and use it like this:
class MyObjectModel(ReadOnlyBaseClass):
def __init__(self, a):
self.a = a
super(MyObjectModel, self).__init__()
After the super call, adding or modifying instance attributes is not possible (... that easily, at least).
A possible caveat I came to think about is that if someone was to modify the __mode attribute and set it to MODE_RO, no new instances could be created. But that seems acceptable since its clearly marked as "private" (in the Pyhon way).
I would be interested if you see any more problems with this solution, or have completely different and better approaches.
Or maybe discourage this at all (with explanation, please)?

How to Elegantly Pass All Attributes of a Class as Arguments in a Function?

I have a somewhat complex class Thing, and an associated mixin IterMixin (to make the class iterable)...and a funky method elsewhere in the codebase which receives an instance of my class as an argument.
In fact, I'm attempting to bundle up a bunch of parameters as single object to be passed to multiple external functions beyond the funky function below. A parameter object design pattern of sorts...
class IterMixin():
def __iter__(self):
for attr, value in self.__dict__.items():
yield attr, value
class Thing(IterMixin):
def __iter__(self, foo=None, bar=None, baz=999):
if foo is None:
self.foo = {}
else:
self.foo = foo
if bar is None:
self.foo = {}
else:
self.bar = bar
self.baz = baz
#property
def foo(self):
return self._foo
#foo.setter
def foo(self, data)
self._foo = self.parser(data)
#property
def bar(self):
return self._bar
#bar.setter
def bar(self, more_data)
self._bar, self.baz = self.another_parser(more_data)
def parser(self, data):
...do stuff...
return foo
def another_parser(self, more_data):
...do add'l stuff...
return bar, baz
With regard to the funky function, in a completely different module, via the Thing class, I want to pass Thing's attributes (foo, bar, and baz) to the funky function as one argument...like so:
thing_args = Thing()
def funky(*thing_args):
...do stuff...
...expecting to manipulate keys from things_arg
...
return whatever
PROBLEM:
If I do not make the setters for the attributes foo and bar private (for example, via self._foo)--i.e., by way of an underscore--then I evoke infinite recursion during class initialization ...as the __init__ and setters for these attributes loop over and over and repeatedly call themselves. To avoid that, I used the#property decorator and "privatized" the foo and bar while setting them.
However, when I pass an instance of the Thing class, and unpack its attributes as args in the funky function via a splat or asterick, if I introspect the resultant keys for those attributes, I still get _foo and _bar. I can't seem to get rid of the underscores. (In other words, I get the "privatized" attribute names of Thing.)
The biz logic of funky needs the unpacked values to not have any underscores.
Why is this happening (the underscores upon unpacking)? How can I fix this? Is there a more elegant way to either initialize the foo and bar attributes without privatizing anything? Or perhaps a more Pythonic way to pass all the attributes in the Thing class to my funky function?
First, you've got a major problem that will prevent you from even seeing the problem you've asked for help with: Your Thing class defines an __iter__ method that doesn't super, and doesn't yield or return anything. Hopefully that part is just some typo and you know how to fix it to do whatever you actually wanted there.
No, onto the problem you're asking about:
class IterMixin():
def __iter__(self):
for attr, value in self.__dict__.items():
yield attr, value
Try printing out the __dict__ of your instances. Or, better, instances of a minimal example like this:
class Thing:
#property
def foo(self):
return self._foo
#foo.setter
def foo(self, data):
self._foo = data
t = Thing()
t.foo = 2
print(t.__dict__)
The output is {'_foo': 2}.
You've tried to hide the attributes by giving them private names and putting them behind properties, but then you've gone around behind the properties' backs and looked directly into the __dict__ where the real attributes are.
And what else could be there? Your actual _foo has to be stored somewhere on each instance. That foo, on the other hand, isn't really a value, it's a getter/setter that uses that private attribute, so it isn't stored anywhere.
If you really want to use reflection to find all of the "public values" on an instance, you can do something like this:
for attr, value in inspect.getmembers(self):
if not attr.startswith('_') and not callable(value):
yield attr, value
However, I think it would be much better to not do this reflectively. Simpler and cleaner options include:
Add a _fields = 'foo', 'bar', 'baz' and have the base class iterate _fields_.
Write a decorator that registers a property, and have the base class iterate that registry.
Build something that lets you specify the attributes more declaratively and writes the boilerplate for you. See namedtuple, dataclass, and attrs for some inspiration.
Just use attrs (or, if you're not the OP but someone reading this from the future who can rely on 3.7+, dataclass) to do that work for you.
Rethink your design. A class whose instances iterate name-value pairs of their public attributes is weird in the first place. A "parameter object" that acted like a mapping to be used for keyword-splatting could be useful; one that acted like a normal iterable could be useful; one that acts as an iterable of name-value pairs is useless for anything except for passing to a dict construct (at which point it's, again, simpler to be a mapping). Plus, a mixin is really not helping you with the hard part of doing it. Whatever you actually need to do, ask for help on how to do that, instead of how to make this code that shouldn't work work anyway.

Python setter to also return a value

I was wondering if it would be possible to have a property setter to also return a value. The code below tries to explain superficially the problem.
Basically I need to set a property to an object, but before I need to check if it is unique. I was wondering if there is a way to the return the unique name to the user directly, without needing to query the object for its new name afterwards.
class MyClass(object):
def __init__(self,ID):
self._ID = ID
#property
def Name(self):
return DBGetName(self._ID)
#Name.setter
def Name(self, value):
UniqueName = DBGetUniqueName(self._ID,value)
return DBSetName(UniqueName)
Myinstance = MyClass(SomeNumber)
#What I do now
Myinstance.Name = "NewName"
Uniquename = Myinstance.Name
#What I was wondering if possible. Line below is syntactically invalid, but shows the idea.
Name = (Myinstance.Name = "NewName")
Edit:
It is a pseudo code and I forgot to actually pass the value to the inner function. My bad.
A setter certainly can return a value.
But it isn't very useful to do so, because setters are generally used in assignment statements—as in your example.
The problem is that in Python, assignment is not an expression, it's a statement. It doesn't have a value that you can use, and you can't embed it in another statement. So, there is no way to write the line you want to write.
You can instead call the setter explicitly… but in that case, it's a lot clearer to just write it as a regular method instead of a property setter.
And in your case, I think a regular method is exactly what you want. You're completely ignoring the value passed to the setter. That's going to confuse readers of your code (including you in 6 months). It's not really a setter at all, it's a function that creates a unique name. So, why not call it that?
def CreateUniqueName(self):
UniqueName = DBGetUniqueName(self._ID)
return DBSetName(UniqueName)
(It's worth noting that DBSetName returning its argument is itself not very Pythonic…)
If you're wondering why Python works this way, see Why can't I use an assignment in an expression? from the official FAQ.
More generally, the expression-statement distinction, together with associated features like mutating methods (e.g., list.sort) returning None instead of self, leads to a simpler, more regular language. Of course there's a cost, in that it leads to a much less fluent language. There's an obvious tradeoff, and Python is about as far to the extreme as you can get. (Compare to JavaScript, which is about as far to the opposite extreme as you can get.) But most people who love Python think it made the right tradeoff.
I started with a similar question, but I only needed to get 'generic' information from a setter in order to automate GUI creation; The GUI code looks through a list of attributes, finds those that have setters and then creates input fields. However some inputs are strings, others floats etc. and the GUI code generates appropriate input fields. I know this is a kludge, but I put that information in the docstring for the getter.
I realise this is not an answer to your question but might help someone looking like I was!
class Dog():
def __init__(self):
self._name = None
return
#property
def name(self):
"""Doggos are the best!"""
return self._name
#name.setter
def name(self, n):
self._name = n
return
def main():
terrier = Dog()
terrier.name = "Rover"
print("Dog's name is ", terrier.name)
print(type(terrier).name.__doc__)
# also works with...
print(getattr(type(terrier), 'name').__doc__)
return
if __name__ == '__main__':
main()
With Python3.8 or greater:
class MyClass(object):
def __init__(self,ID):
self._ID = ID
#property
def Name(self):
return DBGetName(self._ID)
#Name.setter
def Name(self, value):
UniqueName = DBGetUniqueName(self._ID,value)
return DBSetName(UniqueName)
Myinstance = MyClass(SomeNumber)
Myinstance.Name = (Uniquename := Myinstance.Name) # Python >= 3.8

Categories

Resources