Python: how to use some kind of generic setter? - python

I've made this code:
if fc.get('field_photo_1'):
pt.photo1 = fc.get('field_photo_1')
if fc.get('field_photo_2'):
pt.photo2 = fc.get('field_photo_2')
if fc.get('field_photo_3'):
pt.photo3 = fc.get('field_photo_3')
I'd like to optimize it to some kind of code like this:
update_field(photo1, 'field_photo_1'):
update_field(photo2, 'field_photo_2'):
update_field(photo3, 'field_photo_3'):
I just don't know how make kinddof a setter with index in python. How would you implement the update_field() function?

You could take a look at the setattr() function.

I would suggest making an array of photos and then iterate strings with a for loop instead of trying to create such setter. It's easier to understand and doesn't do uncommon things with your class that other programmers might not expect.
class Pt:
def __init__(self):
self.photos = [None for i in range(3)]
pt = Pt()
for i in range(1,4)
fieldString = 'field_photo_{0}'.format(i)
if fc.get(fieldString):
pt.photos[i] = fc.get(fieldString)

You are looking for setattr, which accepts string attribute names.
Try this:
for x in range(1, 4):
attrname = "photo_".format(x)
setattr(pt, attrname, fc["field_".format(attrname)])
You should probably refactor your code though to use lists.

If you are wanting to set the pt attribute based on the passed parameter, setattr is the way to go.
def update_field(attribute, value):
setattr(pt, attribute, value)
The other answers here assume that you are iterating through a preset number of photo attributes. If you are wanting a single set, then the above should work. If looking to set them all, Ben's or Ritave's answer might be more suitable.
Depending on the scope of the "pt" variable. You might have to do something like setattr(self.pt, attribute, value).

You could use a dict:
photos = dict()
photos['photo1'] = fc.get('field_photo_1')
# etc...
Or more concisely:
photos = {photo: fc.get(photo) for photo in ('field_photo_1', 'field_photo_2', 'field_photo_3')}

Related

Python Beginners: Creating dynamic class objects with dynamic attributes with loops

I'm trying to create some simple objects that are defined dynamically through a class - to allow me to rapidly iterate through the creation of all possibilities of these objects.
class NSObjects:
def __init__(self, shape, position, shading):
self.shape = shape
self.position = position
self.shading = shading
def __str__(self):
return '{} - {} - {}'.format(self.shape(), self.position(), self.shading())
def NSGenerator_1():
for i in range (0,3):
obj_1_i = NSObjects(shape_init_top + i, posn_init_top+i, shading_init_top+i)
for i in range (3,6):
obj_1_i = NSObjects(shape_init_mid + i, posn_init_mid+i, shading_init_mid+i)
for i in range (6,9):
obj_1_i = NSObjects(shape_init_mid + i, posn_init_mid+i, shading_init_mid+i)
NSGenerator_1()
print(obj_1_2)
At the moment it is telling me that obj_1_2 doesn't exist. For the purpose of this you can assume that I have defined all the init variables to start at 0, 1 or 2 elsewhere in the code. I am basically trying to create a series of objects which will have properties as defined by a mathematical formula.
Thanks in advance for any help you can provide (I only started coding a few weeks ago so this might be a very silly question!)
You only ever assigned to obj_1_i, not obj_1_2, and it was local to the function. There is no way for Python to tell that the _i was meant as a separate variable instead of part of the longer variable name. For a quick fix, try replacing the
obj_1_i = parts with globals()[f'obj_1_{i}'] =.
But rolling numeric indexes into the variable names like that (_1_2) is a code smell. A better design is to actually use them as indexes to a data structure, like a list or dict.
For example, define
obj = {} at the top level (outside of any class or function).
Then you can replace obj_1_2 everywhere with obj[1, 2], etc. If you wrote them that way,obj[1, i] would work as you expect inside those for loops.

Loop python code

self.textboxAnswer1.resize(100,50)
self.textboxAnswer2.resize(100,50)
self.textboxAnswer3.resize(100,50)
self.textboxAnswer4.resize(100,50)
self.textboxAnswer5.resize(100,50)
self.textboxAnswer6.resize(100,50)
Is there a way to put this code into a loop to become more efficient, it seems very repetitive.
using python
You can access class attributes by getattr:
for i in range(6):
getattr(self, 'textboxAnswer{}'.format(i)).resize(100,50)
Hope this helps!
It actually depends on the formulation of your object, but in this case (under the hypothesis I cannot modify the object for any reason) you can actually use the __dict__ and a regular expression:
import re
class T:
def __init__(self):
self.option1 = "1"
self.option2 = "2"
self.notthis = 3
def test(self):
for k in self.__dict__:
if re.compile("option").match(k): # selects only the attributes that start with "option"
print(self.__dict__[k])
t = T()
t.test()
It prints:
1
2
It is ugly and inefficient but does its job. If you can change how the attributes are stored in your object (using a list, a tuple, or any other collection), then follow that way, please.
Applied to you case it would be (rember to import re):
for k in self.__dict__:
if re.compile("textboxAnswer").match(k):
self.__dict__[k].resize(100, 50)
Stick the textboxAnswers in a loop, then just loop over it:
textboxes = # Add all the textboxes into a list here
for tBox in textboxes:
tBox.resize(100,50)

Python: dynamically create methods based on other classes'

I've looked for quite a while but couldn't find a proper answer to my question:
I have a class containing methods which operate on arrays and I want dynamically create methods with a similar name in another class with a modified output.
I've got something like this so far, can anyone guide me ?
Thanks
Class A():
def__init__(self,array):
self.data = array
def method1(self,*args):
newarray = whatever(self.data,*args)
return newarray
def method2(self,*args):
newarray = whatever2(self.data,*args)
return newarray
I want to be able to use those methods to generate new ones in a more complex class, say:
class B(C): #inherits from C
def __init__(self,[arg1,array]):
#initialize from parent class
C.__init__(self,[arg1,array])
#create new methods for this class using same name
methodnames = [element for element in dir(A) if element[0] != '_']
for methodname in methodnames:
##following works but this is not the output I want here
#self.__dict__[methodname] = getattr(A(array),methodname)
#following doesn't work... at least not as I expect it to
#case1
#self.__dict__[methodname] = [arg1,getattr(A(array),methodname)]
#case2
self.__dict__[methodname] = list([arg1,getattr(A(array),methodname)])
a = array
#following returns a list of [arg1, method] but what I really want is [arg1,newarray]
C([arg1,array]).method1(*args)
OK, so let's try to be clearer:
Class A contains filters, takes an array and applies filter as method, returns filtered data.
Class filters()
def__init__(self,array):
self.data = array
def filter1(self,*args):
newarray = median(self.data,*args)
return newarray
def filter2(self,*args):
newarray = gaussian(self.data,*args)
return newarray
...
In another module, I have class SpecialData, which operates on a list of x,y data (where x and y are iterables, i.e. lists, arrays...). So something like
Class SpecialData():
def __init__(self,[x,y]):
self.data = [x,y]
def power(self,power):
ypow = self.data[1]**power
return [x,pow]
def porod(self):
return [x**4,x**4*y]
....
Now, what I want is to add the filter methods contained in class filters to class SpecialData.
I could, of course do this by re-coding all filters with proper format for SpecialClass. but what I really want, is that each time a new filter is added to class filters, to make it available at runtime in class SpecialData without having to re-hard code the new filter.
So, not being very clever, I tried to read the list of available filters in class filters by:
import filters
filternames = [element for element in dir(filters) if element[0] != '_']
for fitlername in filternames:
generate_filters_in_class_SpecialClass
How do I do this properly ?
I found a number of posts related to this, some using super(), others using SpecialData.dict or even setattr. Since the 2nd seemed more understandable to me, I focused on this one and came up with:
import filters
Class SpecialData():
def __init__(self,[x,y]):
self.data = [x,y]
filternames = [element for element in dir(filters) if element[0] != '_']
for fitlername in filternames:
self.__dict__[fitlername ] = [self.data[0],getattr(filters(self.data[1]),fitlername)]
Of course, this doesn't work, because the list is not callable. If I change the last line to :
self.dict[fitlername ] = list([self.data[0],getattr(filters(self.data[1]),fitlername)])
it returns the method as the 2nd element, rather than the result.
Note that the following works, but this is not what I want...
self.dict[fitlername ] = getattr(filters(self.data[1]),fitlername)
Hope this is clearer now...
I think you are trying to make an advanced use of Python without using/knowing its advanced features, like you are borrowing techniques from another language.
This is not a criticism, but you should have a look on Python tutorial, Python introspection or metaclasses.
I think that if you just complete your knowledge on Python functions you will be easily able to solve your problem in a much simpler way.
Rather than generating a proposed solution, you should make it clearer what you are trying to achieve. Class A is a clear example of the starting point; please post an example of your desired ending point, e.g.
Class B():
def__init__(self,array):
self.data = array
def method1(self,*args):
newarray = ComplexWhatever(self.data,*args)
return newarray
def method2(self,*args):
newarray = EvenBiggerWhatever2(self.data,*args)
return newarray
a = A(input_array)
b = B(input_array)
print(a.method1(args))
print(b.method1(args))
What isn't clear is how you want to "dynamically generate" the new function "ComplexWhatever()" instead of writing the function by hand.

Can I format a variable in python?

Is there a way of formating a variable?
For example, I'd like to automate the creation of a variable named M_color, where M is a string of value "bingo". The final result would be bingo_color.
What should I do if the value of M changes during the execution?
The best solution for this kind of problem is to use dictionaries:
color_of = {}
M = "bingo"
color_of[M] = "red"
print(color_of[M])
If the 'variable' can be an attribute of an object, you could use setattr()
class Color(object):
pass
color = Color()
attr_name = '{foo}_color'.format(foo='bingo')
setattr(color, attr_name, 'red')
Beyond that, you're looking at using eval()

Changing variable names with Python for loops [duplicate]

This question already has answers here:
How do I create variable variables?
(17 answers)
Closed 6 years ago.
I was just wondering if anyone knew of a way to change variable names based off of a for loop for something like this:
for i in range(3)
group+i=self.getGroup(selected, header+i)
so that the names of the variables change to accomodate the data. Thanks!
~Sam
You probably want a dict instead of separate variables. For example
d = {}
for i in range(3):
d["group" + str(i)] = self.getGroup(selected, header+i)
If you insist on actually modifying local variables, you could use the locals function:
for i in range(3):
locals()["group"+str(i)] = self.getGroup(selected, header+i)
On the other hand, if what you actually want is to modify instance variables of the class you're in, then you can use the setattr function
for i in group(3):
setattr(self, "group"+str(i), self.getGroup(selected, header+i)
And of course, I'm assuming with all of these examples that you don't just want a list:
groups = [self.getGroup(i,header+i) for i in range(3)]
Use a list.
groups = [0]*3
for i in xrange(3):
groups[i] = self.getGroup(selected, header + i)
or more "Pythonically":
groups = [self.getGroup(selected, header + i) for i in xrange(3)]
For what it's worth, you could try to create variables the "wrong" way, i.e. by modifying the dictionary which holds their values:
l = locals()
for i in xrange(3):
l['group' + str(i)] = self.getGroup(selected, header + i)
but that's really bad form, and possibly not even guaranteed to work.
Definitely should use a dict using the "group" + str(i) key as described in the accepted solution but I wanted to share a solution using exec. Its a way to parse strings into commands & execute them dynamically. It would allow to create these scalar variable names as per your requirement instead of using a dict. This might help in regards what not to do, and just because you can doesn't mean you should. Its a good solution only if using scalar variables is a hard requirement:
l = locals()
for i in xrange(3):
exec("group" + str(i) + "= self.getGroup(selected, header + i)")
Another example where this could work using a Django model example. The exec alternative solution is commented out and the better way of handling such a case using the dict attribute makes more sense:
Class A(models.Model):
....
def __getitem__(self, item): # a.__getitem__('id')
#exec("attrb = self." + item)
#return attrb
return self.__dict__[item]
It might make more sense to extend from a dictionary in the first place to get setattr and getattr functions.
A situation which involves parsing, for example generating & executing python commands dynamically, exec is what you want :) More on exec here.
It looks like you want to use a list instead:
group=[]
for i in range(3):
group[i]=self.getGroup(selected, header+i)
You could access your class's __dict__ attribute:
for i in range(3)
self.__dict__['group%d' % i]=self.getGroup(selected, header+i)
But why can't you just use an array named group?

Categories