I am having some problem accessing class instances. I am calling the class from a procedure, name of instance is defined in some variable. I want the instance name to be created of that value and then want to access it, but when i access it is giving error. Can some one please help to resolve this issue.
class myclass:
def __init__(self,object):
self.name = object
def mydef():
global a1
b = "a1"
b = myclass(b)
mydef()
print a1.name
Second Problem:
In my actual script, I have to create a large number of such instances from this function (around 100). So defining their name as global would be painful, is there a way i could access those instances outside function without having to declare them as global.
Modification:
class myclass:
def __init__(self,object,typename):
self.name = object
self.typeid = typename
def mydef():
file_han = open(file,"r")
while True:
line = file_han.readline()
if not line:
break
start = line.find('"')
end = line.find('"',start+1)
string_f = line[start+1:end]
myclass(string_f,'a11')
mydef(file)
print def.name
print def.typeid
File Contents are :
a11 "def"
a11 "ghi"
a11 "eff"
Here's how I'd do it. I don't know why you're messing around with globals, if you'd care to explain, I'll update my answer.
class Myclass(object):
def __init__(self, name):
self.name = name
def mydef():
return Myclass("a1")
a1 = mydef()
print a1.name
Gather your instances in a list:
instances = []
for x in range(1000):
instances.append(Myclass("Instance {0}".format(x)))
print instance[42].name
Note the changes:
Class names should be capitalized
Use object as the base class of your classes (since python 2.2, but no longer necessary in 3.x)
Don't shadow the built-in object with your parameter name
Just use the string "a1" directly as a parameter instead of assigning it to a variable
Return something from the function instead of passing the result by global variable
RE: Comment
You haven't said anything about the format of these files, so I'll just give an example where the file to be read contains one class name per line, and nothing else:
def mydef(filename):
ret = []
with open(filename) as f:
for line in f:
# Call `strip` on line to remove newline and surrounding whitespace
ret.append(Myclass(line.strip()))
return ret
So if you have several files and wish to add all your instances from all your files to a large list, do it like this:
instances = []
for filename in ["myfile1", "myfile2", "myfile3"]:
instances.extend(mydef(filename))
RE: OP Edit
def mydef(filename):
ret = []
with open(filename, "r") as file_han:
for line in file_han:
string_f = line.split('"')[1]
ret.append(Myclass(string_f))
return ret
i = mydef("name_of_file")
RE: Comment
Oh, you want to access them by name. Then return a dict instead:
def mydef(filename):
ret = {}
with open(filename, "r") as file_han:
for line in file_han:
string_f = line.split('"')[1]
ret[string_f] = Myclass(string_f)
return ret
i = mydef("name_of_file")
print i["ghi"].name # should print "ghi"
RE: Comment
If I understand you correctly, you want to have it both ways -- index by both line number and name. Well then why don't you return both a list and a dictionary?
def mydef(filename):
d = {}
L = []
with open(filename, "r") as file_han:
for line in file_han:
string_f = line.split('"')[1]
instance = Myclass(string_f)
d[string_f] = instance
L.append(instance)
return L, d
L, d = mydef("name_of_file")
print d["ghi"].name
print L[3]
print L.index(d["ghi"])
You could use class as repository for your instances, for example
class Named(object):
def __init__(self,name):
self.name = name
def __new__(cls,name):
instance = super(type,cls).__new__(cls,name)
setattr(cls,name,instance)
return instance
def __repr__(self):
return 'Named[%s]'%self.name
Named('hello')
Named('x123')
Named('this is not valid attribute name, but also working')
print(Named.hello,Named.x123,getattr(Named,'this is not valid attribute name, but also working'))
Related
I have a class which I want to use to extract data from a text file (already parsed) and I want do so using dynamically created class methods, because otherwise there would be a lot of repetitive code. Each created class method shall be asociated with a specific line of the text file, e.g. '.get_name()' --> read a part of 0th line of text file.
My idea was to use a dictionary for the 'to-be-created' method names and corresponding line.
import sys
import inspect
test_file = [['Name=Jon Hancock'],
['Date=16.08.2020'],
['Author=Donald Duck']]
# intented method names
fn_names = {'get_name': 0, 'get_date': 1, 'get_author': 2}
class Filer():
def __init__(self, file):
self.file = file
def __get_line(cls):
name = sys._getframe().f_code.co_name
line = fn_names[name] # <-- causes error because __get_line is not in fn_names
print(sys._getframe().f_code.co_name) # <-- '__get_line'
print(inspect.currentframe().f_code.co_name) # <-- '__get_line'
return print(cls.file[line][0].split('=')[1])
for key, val in fn_names.items():
setattr(Filer, key, __get_line)
f = Filer(test_file)
f.get_author()
f.get_date()
When I try to access the method name to link the method to the designated line in the text file, I do get an error because the method name is always '__get_line' instead of e.g. 'get_author' (what I had hoped for).
Another way how I thought to solve this was to make '__get_line' accepting an additional argument (line) and set it by passing the val during 'the setattr()' as shown below:
def __get_line(cls, line):
return print(cls.file[line][0].split('=')[1])
and
for key, val in fn_names.items():
setattr(Filer, key, __get_line(val))
however, then Python complains that 1 argument (line) is missing.
Any ideas how to solve that?
I would propose a much simpler solution, based on some assumptions. Your file appears to consist of key-value pairs. You are choosing to map the line number to a function that processes the right hand side of the line past the = symbol. Python does not conventionally use getters. Attributes are much nicer and easier to use. You can have getter-like functionality by using property objects, but you really don't need that here.
class Filer():
def __init__(self, file):
self.file = file
for line in file:
name, value = line[0].split('=', 1)
setattr(self, name.lower(), value)
That's all you need. Now you can use the result:
>>> f = Filer(test_file)
>>> f.author
'Donald Duck'
If you want to have callable methods exactly like the one you propose for each attribute, I would one-up your proposal and not even have a method to begin with. You can actually generate the methods on the fly in __getattr__:
class Filer():
def __init__(self, file):
self.file = file
def __getattr__(self, name):
if name in fn_names:
index = fn_names[name]
def func(self):
print(self.file[index][0].split('=', 1)[1])
func.__name__ = func.__qualname__ = name
return func.__get__(self, type(self))
return super().__getattr__(name)
Calling __get__ is an extra step that makes the function behave as if it were a method of the class all along. It binds the function object to the instance, even through the function is not part of the class.
For example:
>>> f = Filer(test_file)
>>> f.get_author
<bound method get_author of <__main__.Filer object at 0x0000023E7A247748>>
>>> f.get_author()
'Donald Duck'
Consider closing over your keys and values -- note that you can see the below code running at https://ideone.com/qmoZCJ:
import sys
import inspect
test_file = [['Name=Jon Hancock'],
['Date=16.08.2020'],
['Author=Donald Duck']]
# intented method names
fn_names = {'get_name': 0, 'get_date': 1, 'get_author': 2}
class Filer():
def __init__(self, file):
self.file = file
def getter(key, val):
def _get_line(self):
return self.file[val][0].split('=')[1]
return _get_line
for key, val in fn_names.items():
setattr(Filer, key, getter(key, val))
f = Filer(test_file)
print("Author: ", f.get_author())
print("Date: ", f.get_date())
I'm coding a game in Python 3 and I need to create an unknown number of objects with each objects properties based on the contents of a file.
To explain, I'll dump some code here:
class attack(object):
def __init__(self, name, power):
self.name = name
self.element = int(power)
import getline from linecache
Attacks = []
count = 1
while 1==1:
line=getline("Attacks.txt", count)
line = line.rstrip()
if line == "":
break
else:
linelist = line.split()
#something involving "attack(linelist[1], linelist[2])"
Attacks.append(item)
count += 1
"Attacks.txt" contains this:
0 Punch 2
1 Kick 3
2 Throw 4
3 Dropkick 6
4 Uppercut 8
When the code is done, the list "Attacks" should contain 5 attack objects, one for each line of "Attacks.txt" with the listed name and power. The name is for the user only; in the code, each object will only be called for by its place in its list.
The idea is that the end user can change "Attacks.txt" (and other similar files) to add, remove or change entries; that way, they can modify my game without digging around in the actual code.
The issue is I have no idea how to create objects on the fly like this or if I even can. I already have working code that builds a list from a file; the only problem is the object creation.
My question, simply put, is how do I do this?
I had the same problem someday:
How to call class constructor having its name in text variable? [Python]
You obviously have to define classes which names are in file. I assume that is done. And you need to have them in current module namespace globals()
from somelib import Punch, Kick, Throw, Dropkick, Uppercut
globals()[class_name](x, y)
line = getline("Attacks.txt", count)
line = line.rstrip()
linelist = line.split()
class_name = linelist[1]
value = linelist[2]
class_object = globals()[class_name]
item = class_object(value)
# or shortly in one line:
# item = globals()[linelist[1]](linelist[2])
You could create a class like so providing overloading operators to support the operations:
class Operation:
def __init__(self, *header):
self.__dict__ = dict(zip(['attack', 'power'], header))
class Attack:
def __init__(self, *headers):
self.__dict__ = {"attack{}".format(i):Operation(*a) for i, a in enumerate(headers, start=1)}
def __setitem__(self, attack_type, new_power):
self.__dict__ = {a:Operation(attack_type, new_power) if b.attack == attack_type else b for a, b in self.__dict__.items()}
def __getitem__(self, attack):
return [b.power for _, b in self.__dict__.items() if b.attack == attack]
#property
def power_listings(self):
return '\n'.join(['{} {}'.format(*[b.attack, b.power]) for _, b in self.__dict__.items()])
with open('filename.txt') as f:
f = [i.strip('\n').split() for i in f]
a = Attack(*f)
print(a.power_listings)
a['Throw'] = 6 #updating the power of any occurrence of Throw
Output:
Throw 6
Kick 3
Punch 2
Uppercut 8
Dropkick 6
I'm not sure what's happening when I print my dictionary.
In Python 3, I have a dictionary of parse_blast objects called transSwiss. Each object's proteinID is the key with the entire object as the value.
I can print transSwiss in it's entirety and I can also print blasto.protein, but not when I combine them to get a dictionary value. I'm not sure what is happening when I use:
print(transSwiss[blasto.protein])
<__main__.parse_blast object at 0x000000373C5666A0>
Here is the code
class parse_blast(object):
def __init__(self, line):
#Strip end-of-line and split on tabs
self.fields = line.strip("\n").split("\t")
self.transcriptId, self.isoform = self.fields[0].split("|")
self.swissStuff = self.fields[1].split("|")
self.swissProtId = self.swissStuff[3]
self.percentId = self.fields[2]
def filterblast(self):
return float(self.percentId) > 95
class parse_matrix(object):
#Consider __init__ as a Constructor
def __init__(self, matrix_lines):
(self.protein,
self.Sp_ds,
self.Sp_hs,
self.Sp_log,
self.Sp_plat) = matrix_lines.strip("\n").split("\t")
def separate_tuples(one_tuple):
return "\t".join(one_tuple)
blastmap = map(parse_blast, blast_output.readlines())
filtered = filter(parse_blast.filterblast, blastmap)
matrixmap = map(parse_matrix, matrix_output.readlines()[1:])
transSwiss = {blasto.transcriptId:blasto for blasto in filtered}
for matrixo in matrixmap:
print(transSwiss[matrixo.protein])
Because your object is defined by you, you also need to tell python how you want it to print. You can do this by defining a function called "__str__" that returns how you want to print your object.
https://en.wikibooks.org/wiki/Python_Programming/Classes#str
Note: I see that I need to more clearly work out what it is that I want each property/descriptor/class/method to do before I ask how to do it! I don't think my question can be answered at this time. Thanks all for helping me out.
Thanks to icktoofay and BrenBarn, I'm starting to understand discriptors and properties, but now I have a slightly harder question to ask:
I see now how these work:
class Blub(object):
def __get__(self, instance, owner):
print('Blub gets ' + instance._blub)
return instance._blub
def __set__(self, instance, value):
print('Blub becomes ' + value)
instance._blub = value
class Quish(object):
blub = Blub()
def __init__(self, value):
self.blub = value
And how a = Quish('one') works (produces "Blub becomes one") but take a gander at this code:
import os
import glob
class Index(object):
def __init__(self, dir=os.getcwd()):
self.name = dir #index name is directory of indexes
# index is the list of indexes
self.index = glob.glob(os.path.join(self.name, 'BatchStarted*'))
# which is the pointer to the index (index[which] == BatchStarted_12312013_115959.txt)
self.which = 0
# self.file = self.File(self.index[self.which])
def get(self):
return self.index[self.which]
def next(self):
self.which += 1
if self.which < len(self.index):
return self.get()
else:
# loop back to the first
self.which = 0
return None
def back(self):
if self.which > 0:
self.which -= 1
return self.get()
class File(object):
def __init__(self, file):
# if the file exists, we'll use it.
if os.path.isfile(file):
self.name = file
# otherwise, our name is none and we return.
else:
self.name = None
return None
# 'file' attribute is the actual file object
self.file = open(self.name, 'r')
self.line = Lines(self.file)
class Lines(object):
# pass through the actual file object (not filename)
def __init__(self, file):
self.file = file
# line is the list if this file's lines
self.line = self.file.readlines()
self.which = 0
self.extension = Extension(self.line[self.which])
def __get__(self):
return self.line[self.which]
def __set__(self, value):
self.which = value
def next(self):
self.which += 1
return self.__get__()
def back(self):
self.which -= 1
return self.__get__()
class Extension(object):
def __init__(self, lineStr):
# check to make sure a string is passed
if lineStr:
self.lineStr = lineStr
self.line = self.lineStr.split('|')
self.pathStr = self.line[0]
self.path = self.pathStr.split('\\')
self.fileStr = self.path[-1]
self.file = self.fileStr.split('.')
else:
self.lineStr = None
def __get__(self):
self.line = self.lineStr.split('|')
self.pathStr = self.line[0]
self.path = self.pathStr.split('\\')
self.fileStr = self.path[-1]
self.file = self.fileStr.split('.')
return self.file[-1]
def __set__(self, ext):
self.file[-1] = ext
self.fileStr = '.'.join(self.file)
self.path[-1] = fileStr
self.pathStr = '\\'.join(self.path)
self.line[0] = self.pathStr
self.lineStr = '|'.join(self.line)
Firstly, there may be some typos in here because I've been working on it and leaving it half-arsed. That's not my point. My point is that in icktoofay's example, nothing gets passed to Blub(). Is there any way to do what I'm doing here, that is set some "self" attributes and after doing some processing, taking that and passing it to the next class? Would this be better suited for a property?
I would like to have it so that:
>>> i = Index() # i contains list of index files
>>> f = File(i.get()) # f is now one of those files
>>> f.line
'\\\\server\\share\\folder\\file0.txt|Name|Sean|Date|10-20-2000|Type|1'
>>> f.line.extension
'txt'
>>> f.line.extension = 'rtf'
>>> f.line
'\\\\server\\share\\folder\\file0.rtf|Name|Sean|Date|10-20-2000|Type|1'
You can do that, but the issue there is less about properties/descriptors and more about creating classes that give the behavior you want.
So, when you do f.line, that is some object. When you do f.line.extension, that is doing (f.line).extension --- that is, it first evalautes f.line and then gets the extension attribute of whatever f.line is.
The important thing here is that f.line cannot know whether you are later going to try to access its extension. So you can't have f.line do one thing for "plain" f.line and another thing for f.line.extension. The f.line part has to be the same in both, and the extension part can't change that.
The solution for what you seem to want to do is to make f.line return some kind of object that in some way looks or works like a string, but also allows setting attributes and updating itself accordingly. Exactly how you do this depends on how much you need f.lines to behave like a string and how much you need it to do other stuff. Basically you need f.line to be a "gatekeeper" object that handles some operations by acting like a string (e.g., you apparently want it to display as a string), and handles other objects in custom ways (e.g., you apparently want to be able to set an extension attribute on it and have that update its contents).
Here's a simplistic example:
class Line(object):
def __init__(self, txt):
self.base, self.extension = txt.split('.')
def __str__(self):
return self.base + "." + self.extension
Now you can do:
>>> line = Line('file.txt')
>>> print line
file.txt
>>> line.extension
'txt'
>>> line.extension = 'foo'
>>> print line
file.foo
However, notice that I did print line, not just line. By writing a __str__ method, I defined the behavior that happens when you do print line. But if you evaluate it "raw" without printing it, you'll see it's not really a string:
>>> line
<__main__.Line object at 0x000000000233D278>
You could override this behavior as well (by defining __repr__), but do you want to? That depends on how you want to use line. The point is that you need to decide what you want your line to do in what situations, and then craft a class that does that.
I have written an algorithm in Python and now I am trying to make it a bit more object oriented. I have a good understanding (I think) of objects and classes and I have spend some time reading online the syntax for classes in Python. However, I guess my question is quite basic and it would be great to get some help.
I have created a Class XML which contains 3 definitions. I also have used __init__ to initialize the object.
class XML():
def __init__(self,f):
self.f = f
def xmlToString(self):
data = self.f.read()
self.f.close()
...
return station_arr
def exportArray(self):
f= open('stations/'+self.STATION+'.txt')
lines= f.readlines()
...
return phenomena,parameters
def calcAvg(self):
split_phenom = self.phenomena.split(';')
list_of_lists = []
for e in self.parameters:
...
return phenomena,parameters
Then, in the main.py I instantiate the objects and call the methods I want like this:
stations_names ['one', 'two'...]
for station in stations_names:
f = open('respond.txt','r')
xmlStr = ClassXML.XML(f)
stations_arr = xmlStr.xmlToString()
xmlRead = ClassXML.XML(stations_arr)
phenomena,parameters = xmlRead.exportArray()
xmlRetr = ClassXML.XML(phenomena,parameters)
avg_dict,dict_values = xmlRetr.calcAvg()
The error I get is this:
f= open('stations/'+self.station+'.txt')
AttributeError: XML instance has no attribute 'station'
So I understand what is the problem. Some how I have to pass into the class the variable "station". But when I try to included it in the init function I get different errors:
xmlStr = ClassXML.XML(f)
TypeError: __init__() takes exactly 3 arguments (2 given)
Then I thought maybe I have to have multiple init functions but as far as I know this is not possible in Python.
To be honest I don't really know how to handle the problem. Any tip would be useful.
Thanks
D
P.s. I am not sure if the title explains correctly my question, but I can not find any correct words to put it!
IMPLEMENTED FINAL ANSWER
class XML():
def __init__(self,f,station):
self.f = f
self.station =station
def xmlToString(self):
data = self.f.read()
self.f.close()
...
return station_arr
def exportArray(self):
f= open('stations/'+self.STATION+'.txt')
lines= f.readlines()
...
return phenomena,parameters
def calcAvg(self,phenomena,parameters):
split_phenom = self.phenomena.split(';')
list_of_lists = []
for e in self.parameters:
...
return avg_dict,dict_values
** Main **:
for station in stations_names:
f = open('respond.txt','r')
## Instantiate class: ClassXmlString
xmlStr = ClassXML.XML(f,station)
stations_arr = xmlStr.xmlToString()
if stations_arr !='':
phenomena,parameters = xmlStr.exportArray()
avg_dict,dict_values = xmlStr.calcAvg(phenomena,parameters)
class XML():
def __init__(self,f,station):
self.f = f
self.station=station
def xmlToString(self):
data = self.f.read()
self.f.close()
...
self.station_arr = station_arr
def exportArray(self):
#here you need to use self.station_arr
f= open('stations/'+self.station+'.txt')
lines= f.readlines()
...
self.phenomena=phenomena
self.parameters=parameters
def calcAvg(self,):
#here you need to use self.phenomena and self.parameters
split_phenom = self.phenomena.split(';')
list_of_lists = []
for e in self.parameters:
...
self.avg_dict = avg_dict
self.dict_values = dict_values
def makeOutput(self):
#call all your functions
self.xmlToString()
self.exportArray()
self.scalcAvg()
return self.avg_dict , self.dict_values
#now in your main you need to instanciate your class once! not each time you need to call a method:
stations_names ['one', 'two'...]
for station in stations_names:
f = open('respond.txt','r')
xmlStr = ClassXML.XML(f,station)
avg_dict,dict_values = xmlStr.makeOutput()
Haven't tried it, but should work.
I think you could change the way the solution is organized, to make things a bit easier.
Based on the code you posted, I'm assuming that:
xmlToString takes the file f and station as parameter
exportArray takes stations_arr as parameter
calcAvg takes (phenomena, parameters) as parameter
I'll also assume you are ultimately interested in the (avg_dict, dict_values). That said, a slightly refactored version of this solution be something like this:
Main code:
stations_names ['one', 'two'...]
for station in stations_names:
my_xml_object = ClassXML.XML('respond.txt', station)
avg_dict, dict_values = my_xml_object.calcAvg()
Class:
class XML():
def __init__(self, f_name, station):
# 1 - define self.data
with open(f_name, 'r') as f:
self.data = f.read()
# 2 - define self.station_arr
self.station_arr = self.xmlToString(station)
# 3 - Finally define (phenomena, parameters), which
# will be used by calcAvg()
self.phenomena, self.parameters = self.exportArray(station_arr)
def xmlToString(self, station):
data = self.data
...
return station_arr
def exportArray(self, station_arr):
# you must define self.STATION somewhere
f = open('stations/' + self.STATION + '.txt')
lines = f.readlines()
...
return phenomena, parameters
def calcAvg(self):
split_phenom = self.phenomena.split(';')
list_of_lists = []
for e in self.parameters:
...
return phenomena, parameters
I havent't test it, but the most important is that you get the idea.