patternpythonMinor
Changing attributes in different objects
Viewed 0 times
changingdifferentattributesobjects
Problem
First some background - I am developing a Python program which has thousands of lines spread across many files. I have intermediate programming skills - and no commercial OOP experience, and have taught myself Python recently. I have come across a problem which I can solve, in a number of different ways, but I'm not really happy with any of my solutions - they don't feel quite right from an OOP point of view. I have tried to make things generic so they can be used for lots of different options, but by doing so it seems massively convoluted.
I have distilled the problem down into a comparatively small piece of code for review. I have a main object, and I have an object which holds some options (which are just true/false for now, but could be other things in the future). I then have a third object (a tkinter button) that each show the status of the option (via being sunken or raised), and when clicked, change the value.
I felt the right way to do this would be for the functionality (in italics above) to be contained entirely in the
```
from Tkinter import *
class optionsClass():
_OPTION1 = 1
_OPTION2 = 2
def __init__(self):
self.Option1 = True
self.Option2 = False
def change(self, optionid):
if optionid == optionsClass._OPTION1:
if self.Option1 == True:
self.Option1 = False
else:
self.Option1 = True
I have distilled the problem down into a comparatively small piece of code for review. I have a main object, and I have an object which holds some options (which are just true/false for now, but could be other things in the future). I then have a third object (a tkinter button) that each show the status of the option (via being sunken or raised), and when clicked, change the value.
I felt the right way to do this would be for the functionality (in italics above) to be contained entirely in the
toggleButton class. At first I just passed it self.options.Option1 (or whatever), but that didn't work. I then read up on does Python pass by value or reference, which (I know I am simplifying here) seemed to say that when you pass an object it passes the object, but when you pass something like an integer or boolean it passes the value. So I changed it to what you see now - where you pass the variable, an integer representing the variable, and changing the options class to have a function to change the option. It all seems so needlessly verbose. Is there a better way?```
from Tkinter import *
class optionsClass():
_OPTION1 = 1
_OPTION2 = 2
def __init__(self):
self.Option1 = True
self.Option2 = False
def change(self, optionid):
if optionid == optionsClass._OPTION1:
if self.Option1 == True:
self.Option1 = False
else:
self.Option1 = True
Solution
First, to answer your question on passing by value/reference, Python doesn't pass mutable (e.g.
When you pass an immutable object:
In this case, the string is immutable, it can't be changed in-place, so changes in
By contrast, with a mutable type:
You should read and consider following the Python Style Guide, PEP-0008 - class names should be
can be simplified to:
Dictionaries would allow you to simplify further:
Now you can use it:
list, dict) and immutable (e.g. str, tuple) objects any differently - the difference appears because they are[n't] mutable, i.e. can[not] be changed in-place. Names in Python are just references to objects. When you pass an immutable object:
def calling():
foo = "bar" # bind str object to name 'foo' in scope of 'calling'
called(foo) # pass reference to str object to 'called'
print(foo) # still "bar"
def called(baz): # first arg object will be bound to name 'baz' in scope of 'called'
baz = "{0}!".format(baz) # rebinds name 'baz' to **new str object**
print(baz)In this case, the string is immutable, it can't be changed in-place, so changes in
called won't be reflected in calling, as calling still references the original object. By contrast, with a mutable type:
def calling():
foo = [] # bind list object to name 'foo' in scope of calling function
called(foo) # pass reference to list object to 'called'
print(foo) # see change from 'called'
def called(baz): # first arg object will be bound to name 'baz' in scope of 'called'
baz.append("hello, world") # object bound to name 'baz' (and 'foo') **is changed**
print(baz)You should read and consider following the Python Style Guide, PEP-0008 - class names should be
CapitalizedWords and variable, function and method names should be lowercase_with_underscores.optionsClass can be streamlined. At the very least, note thatif self.Option1 == True:
self.Option1 = False
else:
self.Option1 = Truecan be simplified to:
self.Option1 = not self.Option1Dictionaries would allow you to simplify further:
class OptionsClass():
id_map = {'Option1': 1, 'Option2': 2}
def __init__(self, options=None):
if options is None:
options = {1: True, 2: False}
self.options = options
def change(self, option_id):
self.options[option_id] = not self.options[option_id]
def __getattr__(self, attr_name):
return self.options[id_map[attr_name]]Now you can use it:
>>> o = OptionsClass()
>>> o.Option1
True
>>> o.Option2
False
>>> o.change(1)
>>> o.Option1
FalseCode Snippets
def calling():
foo = "bar" # bind str object to name 'foo' in scope of 'calling'
called(foo) # pass reference to str object to 'called'
print(foo) # still "bar"
def called(baz): # first arg object will be bound to name 'baz' in scope of 'called'
baz = "{0}!".format(baz) # rebinds name 'baz' to **new str object**
print(baz)def calling():
foo = [] # bind list object to name 'foo' in scope of calling function
called(foo) # pass reference to list object to 'called'
print(foo) # see change from 'called'
def called(baz): # first arg object will be bound to name 'baz' in scope of 'called'
baz.append("hello, world") # object bound to name 'baz' (and 'foo') **is changed**
print(baz)if self.Option1 == True:
self.Option1 = False
else:
self.Option1 = Trueself.Option1 = not self.Option1class OptionsClass():
id_map = {'Option1': 1, 'Option2': 2}
def __init__(self, options=None):
if options is None:
options = {1: True, 2: False}
self.options = options
def change(self, option_id):
self.options[option_id] = not self.options[option_id]
def __getattr__(self, attr_name):
return self.options[id_map[attr_name]]Context
StackExchange Code Review Q#52313, answer score: 4
Revisions (0)
No revisions yet.