@property or getters/setters?

69 views
Skip to first unread message

Rudi Hammad

unread,
Aug 6, 2020, 4:39:58 AM8/6/20
to Python Programming for Autodesk Maya
Hello,
first of all, I've been reading many discussions about when to use what. Here is a recap of what I've reasearch:
.Don't use getters/setters in python, because those are from other idioms. getters and setters are meant are use to access private attributes, but since in python there is no real encapsulation (it is a naming convention), there is no need.
.Properties are cleaner. E.g: myNodeA.getx() + myNodeB.getx() is not as clear as myNodeA.x + myNodeB.x
.Use properties only for real only attributes (but doesn't that contradict the example above? we can do myNodeA.x = 5
.If someone decide to convert an attribute into a property (e.g, instead of self.myName, me want a funcion def myName(), using @property will not break the code.

Will all that info, I still had doubts. So I decided to look at OpenMaya 2.0 which is meant to be pythonic. So here are some examples
MBoundingBox has the following as properties --> center, depth, height, max, min, width. That make sense because those are only readable I guess. You can't set them as you want, si it doesn't make sense to do getCenter(), getDepth()....
MDagPath has no properties at all. It has .child(), childCount(), apiType().... but aren't those readable only too?! so why not just child, childCount, apiType.
MObject has as property apiTypeStr, but a method called apiType() !! what the hell XD? why don't do both as properties?

So yea..looking at OpenMaya 2.0 did not make things more clear.
Here is how I use properties:
. I don't like node.getName() and node.setName("foo"), I prefer node.name and node.name = "foo". I find it cleaner.
. If I have different option of getting something, I need an argument, so I don't use property. E.g: node.getMatrix(world=True), node.getMatrix(world=False).
. in scientif libraries: I do vector.magnitude, vector.magnitude = 10, instead of vector.getMagnitude(), vector.setMagnitude(10)

What is your take on that? Cheers,
R

Justin Israel

unread,
Aug 6, 2020, 7:47:35 AM8/6/20
to python_in...@googlegroups.com
Here is my opinion. And it's not based on PEP or the Maya SDK api; just my experience to date.
Sure Python has protected and private concepts as a naming convention only. But I see properties as sugar for getters/setters(/deleters). I don't associate them specifically with read-only attributes. To me, they replicate a C/C++ convention for controlling the read and writes of an attribute in order to validate the access, or to provide a computed result. 
In the first case of controlling access, that would mean you want to be able to check the value before writing it, or have a hook before reading, if say it were not yet initialised. 
In the second case, you have a value that is dynamically computed. Such as a "fullname" property that is really just a concatenation of the first and last name attributes. Or if you want to cache the value after the first access and then return that cached value on subsequent calls. 
So a getter/setter is mostly the same as a property in effect. But syntactically it routes through attribute access instead of function calls (if that makes any difference to your API). I use properties for both dynamic attributes (computed/cached) and read only attributes (no setter), and/or attributes that are wrapped in caching.

Hope my 2cents is useful.

Justin 


--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/c34f5e0c-2b7a-42a5-856d-84b38ad5f5c3o%40googlegroups.com.

Rudi Hammad

unread,
Aug 6, 2020, 10:07:57 AM8/6/20
to Python Programming for Autodesk Maya
class Person(object):
    
    def __init__(self, name, lastName):
        self.__name = name
        self.__lastName = lastName
        
    
    # dynamic attributes
    @property
    def name(self):
        return self.__name
    
    @property
    def lastName(self):
        return self.__lastName
        

    @name.setter
    def name(self, value):
        self.__name = value
        return self.__name
    
    @lastName.setter
    def lastName(self, value):
        self.__lastName = value
        return self.__lastName

    # read only
    @property
    def specie(self):
        return "Human"
        
    # method
    def fullName(self):
        return self.name + " " + self.lastName
        
person = Person("Homer", "Simpson")
person.name # Result: Homer #
person.lastName # Result: Simpson #
person.fullName() # Result: Homer Simpson #
person.name = "Bart"
person.fullName() # Result: Bart Simpson # <-- dinamically computed because name and lastName are properties
person.specie # Result: Human #
person.specie = "Alien"  # AttributeError: can't set attribute # <-- which is fine, because it is read only


Rudi Hammad

unread,
Aug 6, 2020, 10:09:55 AM8/6/20
to Python Programming for Autodesk Maya
Uppss, I pasted the code as a text, sorry, and deleted my coment about it.
I was saying that I use properties as you do I think. And I posted the example above with the cases you described to see if I understood you well.

Cheers

Marcus Ottosson

unread,
Aug 6, 2020, 2:52:43 PM8/6/20
to python_in...@googlegroups.com

Nowadays, when I get the chance, I tend to prefer accessing data either via a dictionary, or a dictionary-like interface.

data = {}
data["name"] = "Marcus"

For classes, that would then look like..

self._data["name"] = "Marcus"

For example..

widgets["button"].clicked.connect(self.on_clicked)

layout = QtWidgets.QHBoxLayout(panels["footer"])
layout.addWidget(widgets["button"])

I find this extends well to Maya attributes.

import cmdx
node, = cmdx.selection()
node["translateX"] = 5.0
print(node["rx"])
node["newAttribute"] = cmdx.Double(default=6.0)
print(node.path(namespace=True))

That way, I can have this rule.

node.doSomething()
node["storeSomething"]

I mostly avoid @property actually, I haven’t really had a good experience with those. They’re good on paper.. I like that they prevent accidental assignment when you e.g. misspell.

node.misspeled = "Bad"
# Error

But I don’t like how much extra typing you need (like your example above), and I most of all don’t like not knowing whether calling your @property incurs a cost or not.

print(some_class.distance)

Did this return a precomputed distance, or was one computed as I requested it?

At the end of the day though, most of the time I work in a code base that isn’t mine and follow whatever convention is already established. Other times I know the code I write will be edited by others and pick a convention I expect will be the least surprising and the least distracting.

Your mileage may vary, as they say. :)


--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

Justin Israel

unread,
Aug 6, 2020, 4:42:18 PM8/6/20
to python_in...@googlegroups.com
@rudi 
So in your example I would say the name and lastName properties are a perfect example of where I would not use a property, since defining a getter and setter for those attributes without doing anything else but getting and setting the value provides no benefit of just exposing the public attributes in the first place. More likely I would expect to see something like this:

class Person(object):

    def __init__(self, name, lastName):

        self.name = name
        self.lastName = lastName

    @property

    def fullName(self):
        return self.name + " " + self.lastName
Then you can access the very inexpensive, but dynamic, fullName computed property.

The use of the read-only specie property is something I would use.
Also, property setters don't need to return a value.

@marcus 
I completely agree with you about avoiding properties that are expensive and pretend to look like normal attribute access. In a particular API, I recently did something like this:
obj.attr1.attr2.foo()
But it turns out that attr1 is a property and on its first access it does a big expensive resolve operation, with various side effects. That kind of thing should not be hidden behind a property that will look like a simple attribute access. It should be a method.

About the dictionary usage, where you gain flexibility I think you lose safety. It would be easier to make a typo when using string literals constantly to access the dictionary, and an IDE won't be able to help you catch that. So then if you end up defining the keys as constants then you are right back to just having actual attributes. But I get that in certain situations you don't know the shape of the data and dict would be the right kind of field.

Rudi Hammad

unread,
Aug 6, 2020, 5:51:12 PM8/6/20
to Python Programming for Autodesk Maya
That's am interesting aproach Marcus. Didn't thought about it.

After writinh that example, I was driving and I thought... wait a minute...that is example is so simple that just setting name and lastName as public properties is the same. I swear I was goinh to
reply with that very same example you did! XD
That was a bad example, but the logic was very simple. What about this random example? :
class Foo(object):

   
def __init__(self, input_):
       
self.input =  input_
   
   
@property
   
def x(self):
        trans
= cmds.getAttr(self.input + ".tx")
       
return trans
   
   
@x.setter
   
def x(self, value):
       
if self.x >= 0:
            cmds
.setAttr(self.input + ".tx", value)
       
else:
           
pass
   
   
jnt
= cmds.joint()    
foo
= Foo(jnt)
foo
.x
foo
.x = 5
foo
.x = foo.x + 10


# vs

class Foo(object):

   
def __init__(self, input_):
       
self.input =  input_
   
   
def getX(self):
        trans
= cmds.getAttr(self.input + ".tx")
       
return trans
   
   
def setX(self, value):
       
if self.getX() >= 0:
            cmds
.setAttr(self.input + ".tx", value)
       
else:
           
pass


jnt
= cmds.joint()    
foo
= Foo(jnt)
foo
.getX()
foo
.setX(5)
foo
.setX(foo.getX() + 10)


I think almost every one can agree that foo.x = foo.x + 10 is a much more clear than foo.setX(foo.getX() + 10)
So this example isn't a read only attribute case. You can get and set. Aren't cases like that aslo a good use of properties?
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_maya+unsub...@googlegroups.com.

Justin Israel

unread,
Aug 6, 2020, 6:20:31 PM8/6/20
to python_in...@googlegroups.com
Ya this is a likely case where I might expect properties to be used. 
 
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python_inside_m...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/7f1f0385-417c-4895-936d-0426c2ccd7c7o%40googlegroups.com.

Marcus Ottosson

unread,
Aug 7, 2020, 2:15:14 AM8/7/20
to python_in...@googlegroups.com
> and an IDE won't be able to help you catch that

Ooo, yes, good point. This does become a problem sometimes.

Reply all
Reply to author
Forward
0 new messages