Can you make an instanced object accessing an attribute be variable? - python

Consider my following function. The argument takes in a string or int, and it compares the input to each excel cell value, starting from the first row and goes to the rightmost column and then going down to the last row. If a cell matches with the argument, then it returns information about the position of cell. If there is more than one match, than it continues to append the triplet to the information list.
My question is, is it possible to make an object instance, an argument of the function? Because in my function, the object, "ws", that is being dotted with an attribute or method "iter_rows" is static. So everytime I call this function, it will only access "ws". I would like it if there was a way to change the object when you are calling the function. By perhaps adding a second argument to the function and making the instantiated object a "variable". Though from my testing, it seems like this is not possible. I am not sure if what I'm trying to do is not allowed in general or it's just this openpyxl class. The obvious naive alternative solution is making a separate function called something like "locate2" and changing "ws" to "ws1". Is there any way to implement this without creating another function and hard coding the instances?
def locate(val):
position_info = []
for row_s in ws.iter_rows(min_col=1, max_col=ws.max_column):
for cell_s in row_s:
if cell_s.value == val:
position_info.append([cell_s.row, cell_s.column, cell_s.coordinate])
if not position_info:
print("There is no cell with this value.")
else:
return position_info
path = 'C:\test.xlsx'
path2 = 'C:\test1.xlsx'
wb = xl.load_workbook(path, data_only=True)
ws = wb.worksheets[0]
wb1 = xl.load_workbook(path2, data_only=True)
ws1 = wb1.worksheets[0]
print(locate("hello"))
I tried to make the object a variable so I can choose what the object can be. But I kept getting attribute error.

Related

Auto expand table range in excel

Tying to find to an Excel VBA equivalent to
sheet.range('A1').expand('table')
#https://docs.xlwings.org/en/stable/datastructures.html
I've tried to create a xlwings func like this :
#xw.func
def expand(rng, caller):
sht = caller.sheet
return sht.range(rng).expand().address
=expand("C7") returns "$C$7:$E$8" (works)
So I've tried for feed this rng as string inside the following macro (that spots changes within a range)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng_s As String
rng_s = expand("C7") #This is where there is the error
Set rng = Target.Worksheet.Range(rng_s)
If Not Intersect(Target, rng) Is Nothing Then my_macro2 (rng)
End Sub
#The Python console returns : TypeError: The Python instance can not be converted to a COM object
Any idea how to make this table expand automatically? Or make this xlwings function work?
You need to call the function directly instead of using xlwings way as they need to correct the code in order to support calling from another function/sub.
Use this in your function instead of calling expand
rng_s = Py.CallUDF("udftest", "expand", Array("C7", Nothing), ThisWorkbook, ThisWorkbook.ActiveSheet.Range("C7"))
Change ThisWorkbook.ActiveSheet for the Sheet where the range you are looking for belongs, that last parameter is passed as the caller in the defined python function, so even you could use only that parameter do get the desired range in your expand function.

Member Variables in Python?

I'm trying to create a console input program in which a user can add HTML to a file.
So if you pressed B(for bold)
and then your input was "test"
It would put "test" into the file.
The problem is, there is only one function "SaveToFile" that actually should be saving it to the file, and until that function is called, I'm expected to keep the output queued up somehow to later write onto the file, or to clear instead of putting into the file.
I know java and C# so my thought was to have some type of member variable
and just concatenate the lines:
mOutput += ("<B>"+userinput+"<B/>);
Any idea how I would achieve something like this in python?
Pretty much the same way as in the other languages. The differences are:
In a Python method, you always have to refer to a member variable (generally called instance attributes) by specifying the object it's attached to, even if it's your own instance. This is achieved by Python automatically passing a reference to your instance as the first argument to your method, which is by convention named self. So instead of mOutput you would probably write self.output (no Hungarian necessary since the self makes it obvious that it's a member variable).
The container you want to use for this is a list. You call the list's append method to add an item to it. Lists also support += but this expects a sequence, not a single item. Worse, strings are considered sequences of characters, so using += to append a string to a list would append the individual characters of the string as separate items! You could make the second argument a sequence by writing e.g. container += [item_to_add], but that forces Python to construct a second list for no good reason.
Instance attributes must be explicitly initialized at instantiation. If you define the attribute on the class, such as writing output = [] in the class definition, that attribute is shared among all instances of the class (unless the instances override it by providing an attribute with the same name). Instead, write self.output = [] in your __init__ method.
You can use the join method of strings to join a sequence together. The string specified is the delimiter between the joined elements. (This is backward from most languages, but makes sense after a while.)
Putting this all together:
class MyObject(object):
def __init__(self):
self.output = [] # empty list
def bold(self, userinput):
self.output.append("<B>" + userinput + "</B>")
def save(self, filename):
with open(filename, "w") as outfile:
# write items separated by line breaks
outfile.write("\n".join(self.output))
Or something like that.

How do I change where a variable is pointing to within a function?

I have a python function that takes an object and either modifies it or replaces it.
def maybe_replace_corgi(corgi):
# If corgi is cute enough, modify it to keep it.
if corgi.cuteness > threshold:
corgi.will_keep = True
else:
# Corgi not cute enough! Instantiate new corgi.
corgi = Corgi(name="Woofus")
I know that objects are passed into python functions by reference. But what if I want to replace an object within a function entirely? As I want to do in my else statement? How do I make all references to the corgi object in my program point to this new corgi object?
The standard way would be to return a new corgi or the old corgi:
def maybe_replace_corgi(corgi):
if corgi.cuteness > threshold:
corgi.will_keep = True
return corgi
else:
return Corgi(name="Woofus")
my_corgi = Corgi(name="Rudolf")
my_corgi = maybe_replace_corgi(my_corgi)
The downside of this is, as mentioned by others: It doesn't replace all references, it only replaces that one reference of my_corgi. However, replacing all references is not possible.
Instead of replacing all references to your object, you could just edit your object to look just like a new one. Just replace all it's attributes' values. For this you could create a new method in Corgi class, which resets all attributes.
In most cases you don't need a new method, you already got one: If your __init__ method doesn't do anything too fancy (fe. increase Corgi.count variable or such) it can be called again to re-init all of your object's attributes.
def maybe_replace_corgi(corgi):
if corgi.cuteness > treshold:
corgi.will_keep = true
else:
corgi.__init__(name="Woofus")
You can't. The only things you can do are to mutate that object so it has the new value you want (as you do in the first part of your if statement), or return it, or store it somewhere that other code will look for it when it wants the value.
For instance, if you do your work in a class, and always set/access the corgi as self.corgi, then doing self.corgi = Corgi(name="Woofus") will work, since subsequent reads of self.corgi will read the new value. A similar effect can be achieved by having the caller do corgi = maybe_replace_corgi(corgi), and having maybe_replace_corgi return the old corgi or a new one.
In general you can't do things like "change all references everywhere in the program". If you are judicious in not creating tons of references to the same object, and instead create only a limited number of references in particular contexts, then you will have no problem changing those references when the time comes.

Deleting certain instances of a class attribute

I am working with classes in Python for the first time and I need to loop through my class attributes and delete certain instances under certain conditions. The problem is that I cannot find any examples of deleting instances of certain attributes. To be a little more specific, my class is phone_bills and one of my attributes is minutes_used and the instance would be minutes used of a specific month. Well, sometimes I need to delete that one month or one instance.
I am starting to wonder if working with classes is wrong for this particular project.
Here is some of my code (where i make the class and then at the bottom where i try to deltete an instance.
class MeterBill:
'components of MeterBill'
def __init__(self,IDC,Name,StartD,End_D,Mdays,Cons): #Name,StartD,End_D,Mdays,Cons):
self.IDC = IDC #idc
self.Name= Name #name
self.StartD = StartD #startd
self.End_D = End_D #end_d
self.Mdays = Mdays #modays
self.Cons = Cons #cons
def __repr__(self):
return repr((self.IDC,self.Name,self.StartD,self.End_D,self.Mdays,self.Cons))
#there is some other code here to read in the data then
e=len(bills); startlen=e;s=0
row=0; daysthresh=38; count=0
while count < startlen:
for row in range(s,e):
count = 1+ count
if bills[row-1].Mdays < daysthresh and bills[row-1].IDC==bills[row].IDC:
print bills[row-1],#row-1,meter[row-1]
bills[row].Mdays = bills[row-1].Mdays+bills[row].Mdays
bills[row].Cons = bills[row-1].Cons+bills[row].Cons
bills[row].StartD=bills[row-1].StartD
#del mybills.minutes_used
#bills=MeterBill()
del bills[row-1].Cons
the last 3 lines is me trying to delte an instance of my class at row-1 (using code from Peter Downs post). I want to delete this one line. I want to delete 1 single instance of each attribute that i defined.
so if I could get that del bill[row-1].cons to work then i would do it for all the other attributes at row-1.
Note you have to scroll to the right ot see my if statement.
I am starting to wonder if working with classes is wrong for this particular project.
No, certainly not, no worries :)
Lets say we have the following class:
class PhoneBills(object):
def __init__(self, minutes_used):
self.minutes_used = minutes_used
Then you can delete the attribute minutes_used simply by:
mybills = PhoneBills()
del mybills.minutes_used
Which would remove the attribute from your object mybills. All lookups would result in an exception. I'm not sure that this is what you want. Probably just setting minutes_used to 0 or None would be a better approach?
Using an object in this case isn't a bad idea, as others have pointed out. However, you have to think about the problem just a little bit differently in order to get the best use of these objects.
If you have a phone_bills object, then we assume its only responsibility is to manage a phone bill. It could be for a single month, it could be for an entire year - there's enough context in the object name that it could do both.
If it's managing a month-to-month bill, then what's required is, at the end of every month, the minutes used is recalculated, meaning that the value for the used minutes at this current point is reset, not deleted.
Now, unless your assignment specifically calls for you to delete the end-of-month total, then you're best served with resetting the value. The way to do this with Python objects is simple:
phone_bills.minutes_used = 0
Deleting means dereferencing the object, till its referencecounter reaches 0, so the garabage collector may free the allocated space for that particular object and effectivly destroying/deleting it.
What you want to do is set the appropriate attribute to None. By doing so, you reduce the refcounter by 1.
To illustrate what I mean, try the following:
import sys
ref = 'a Random object'
print sys.getrefcount(ref), id(ref)
newlist = [ref]
print sys.getrefcount(newlist[0]), id(newlist[0])
ref = None
print sys.getrefcount(newlist[0]), id(newlist[0])
newlist[0] = None
after the last line you have no reference to the underlying object, the refounter reaches 0 and the object gets collected by the garbage collector.
You may also use the del statement to express your intend clearly in your sourecode. e.g.: del june but then you also remove the identifier june from your namespace. Don't shoot the messanger! ;-)
I am starting to wonder if working with classes is wrong for this
particular project.
I believe that they may be unnecessary.
Instead of a class, try using dictionaries.
You could write a helper function to populate them, and it's very easy to remove a key from a dictionary.
The only reason to use a class is if you need instance methods - is that true?
Event then, you could rewrite them as regular functions.
def createPhoneBill(minutes_used):
return {
"minutes_used":minutes_used,
# you could put more attributes here, just add them as arguments to the function
}
As an added bonus, default values are much easier. Also, you get to use the dictionary.get(attr_name, default_value) function now, too.
Here's what deletion would look like:
Deleting an attribute:
mybills = createPhoneBill(5000):
if mybills["minutes_used"] > 2000:
del mybills["minutes_used"]
Deleting an 'instance':
mybills = createPhoneBill(5000):
if mybills["minutes_used"] > 3000:
del mybills
In Python, you don't delete objects--you simply remove any references towards them and allow the garbage collector to reclaim the memory they're holding.
Assigning phone_bills_obj.minutes_used = None would cause the garbage collector to remove the object referenced by phone_bills_ojb.minutes_used, in case the same object isn't being referenced anywhere else.

Python: Efficient way to put multiple variables through a function

I have a bunch of variables that are equal to values pulled from a database. Sometimes, the database doesn't have a value and returns "NoneType". I'm taking these variables and using them to build an XML file. When the variable is NoneType, it causes the XML value to read "None" rather than blank as I'd prefer.
My question is: Is there an efficient way to go through all the variables at once and search for a NoneType and, if found, turn it to a blank string?
ex.
from types import *
[Connection to database omitted]
color = database.color
size = database.size
shape = database.shape
name = database.name
... etc
I could obviously do something like this:
if type(color) is NoneType:
color = ""
but that would become tedious for the 15+ variables I have. Is there a more efficient way to go through and check each variable for it's type and then correct it, if necessary? Something like creating a function to do the check/correction and having an automated way of passing each variable through that function?
All the solutions given here will make your code shorter and less tedious, but if you really have a lot of variables I think you will appreciate this, since it won't make you add even a single extra character of code for each variable:
class NoneWrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __getattr__(self, name):
value = getattr(self.wrapped, name)
if value is None:
return ''
else:
return value
mydb = NoneWrapper(database)
color = mydb.color
size = mydb.size
shape = mydb.shape
name = mydb.name
# All of these will be set to an empty string if their
# original value in the database is none
Edit
I thought it was obvious, but I keep forgetting it takes time until all the fun Python magickery becomes a second nature. :) So how NoneWrapper does its magic? It's very simple, really. Each python class can define some "special" methods names that are easy to identify, because they are always surrounded by two underscores from each side. The most common and well-known of these methods is __init__(), which initializes each instance of the class, but there are many other useful special methods, and one of them is __getattr__(). This method is called whenever someone tries to access an attribute. of an instance of your class, and you can customize it to customize attribute access.
What NoneWrapper does is to override getattr, so whenever someone tries to read an attribute of mydb (which is a NoneWrapper instance), it reads the attribute with the specified name from the wrapped object (in this case, database) and return it - unless it's value is None, in which case it returns an empty string.
I should add here that both object variables and methods are attributes, and, in fact, for Python they are essentially the same thing: all attributes are variables that could be changed, and methods just happen to be variables that have their value set to a function of special type (bound method). So you can also use getattr() to control access to functions, which could lead to many interesting uses.
The way I would do it, although I don't know if it is the best, would be to put the variables you want to check and then use a for statement to iterate through the list.
check_vars = [color,size,shape,name]
for var in check_vars:
if type(var) is NoneType:
var = ""
To add variables all you have to do is add them to the list.
If you're already getting them one at a time, it's not that much longer to write:
def none_to_blank(value):
if value is None:
return ""
return value
color = none_to_blank(database.color)
size = none_to_blank(database.size)
shape = none_to_blank(database.shape)
name = none_to_blank(database.name)
Incidentally, use of "import *" is generally discouraged. Import only what you're using.
you can simply use:
color = database.color or ""
another way is to use a function:
def filter_None(var):
"" if (a is None) else a
color = filter_None(database.color)
I don't know how the database object is structured but another solution is to modify the database object like:
def myget(self, varname):
value = self.__dict__[varname]
return "" if (value is None) else value
DataBase.myget = myget
database = DataBase(...)
[...]
color = database.myget("color")
you can do better using descriptors or properties

Categories