Updating __init__ elements through a class method in Python - python

I have a class that takes a list of strings as an argument and I would like to have
my class methods update the element in the init method. Not sure if this is possible or bad practice, but any advice would be really appreciated! The code would be like:
class TextList:
def __init__(self, listofstrings):
self.strings = listofstrings
def my_method(self):
newstrings = [i.strip() for i in self.strings]
# code that updates 'self.strings' to equal 'newstrings'
So that I could run:
mylist = TextList(mylistofstrings)
mylist = mylist.my_method()
And mylist.strings would equal the output of 'my_method'
Thanks again!

If you just want to update the existing list, you can do that directly.
def my_method(self):
self.strings = [i.strip() for i in self.strings]
Since this method updates self.strings directly, there is no need to return anything.
>>> mylist = TextList(["hi\n", "bye\n"])
>>> mylist.strings
['hi\n', 'bye\n']
>>> mylist.my_method()
>>> mylist.strings
['hi', 'bye']

Related

Python3 script using SQLAlchemy returns object address vice string [duplicate]

I have a function below which I want to output lines of values relating to 'O', instead it prints the location of these values, how do I amend this? allReactions is an empty array initially. I've tried a number of ways to get around this but keep getting errors. Also I think my methods are less efficient than can be.
allReactions = []
reactionFile = "/Databases/reactionsDatabase.txt"
with open(reactionFile) as sourceFile:
for line in sourceFile:
if line[0] == "!" or len(line.strip()) == 0: continue
allReactions.append(Reaction(line, sourceType="Unified Data"))
def find_allReactions(allReactions, reactant_set):
reactant_set = set(reactant_set)
relevant_reactions = []
previous_reactant_count = None
while len(reactant_set) != previous_reactant_count:
previous_reactant_count = len(reactant_set)
for reaction in allReactions:
if set(reaction.reactants).issubset(reactant_set):
relevant_reactions.append(reaction)
reactant_set = reactant_set.union(set(reaction.products))
return relevant_reactions
print find_allReactions(allReactions, ["O"])
You are trying to print a list of Reaction objects. By default, python prints a class object's ID because it really doesn't have much to say about it. If you have control over the class definition, you can change that by adding __str__ and __repr__ method to the class.
>>> class C(object):
... pass
...
>>> print C()
<__main__.C object at 0x7fbe3af3f9d0>
>>> class C(object):
... def __str__(self):
... return "A C Object"
...
>>> print C()
A C Object
>>>
If you don't have control of the class... well, the author didn't implement a pretty view of the class. You could create subclasses with the methods or write a function to pull out the stuff you want.

Is there a way to make the keys of a class iterable?

I'm need to read different datasets, and all of them have some equal properties (e.g. ID and name) and some unique properties. I know that I can build a different function to read each dataset, but I was wondering if it is possible to build a generic dataset reader if I use something like this
My class:
def MyClass():
def __init(self):
self.default_prop1 = ''
self.default_prop2 = ''
My main file:
def main():
keys = ['default_prop1', 'default_prop2', 'not_default_prop1', 'not_default_prop2' ]
obj_myclass = MyClass()
for i in keys:
#Here
obj_myclass[i] = file.readline()
Is there a way to do something like this?
I'll update your class a little bit:
def Car(): #an example of a car class
def __init(self, props):
self.props = ({}, {})
Now you can iterate over the default properties and the extra ones:
def main()
new_car = Car(({"year": 1998}, {"sports_car_type": "countach"}))
# Now, you can go through the keys in both dictionaries of this new object
print("defaults:")
for key, val in new_car.props[0].items():
print(key, val)
print("~~~~~~~~~\extras:")
for key, val in new_car.props[1].items():
print(key, val)
main()
You can use the vars() mechanism. Fixing two typos in your sample code, to give
class MyClass(): # not def
def __init__(self): # not __init
self.default_prop1 = ''
self.default_prop2 = ''
you can do
>>> mc = MyClass()
>>> vars(mc)
{'default_prop1': '', 'default_prop2': ''}
The object returned by vars() is a proper dict (it returns the __dict__ attribute) and can be updated the way you want.
>>> vars(mc)["new_prop"] = "Fred"
>>> mc.new_prop
'Fred'
Or, if you want to do it in a loop:
>>> for i in (v := vars(mc)):
v[i] = file.readline()

Appending to array in object appends to all instances

I'm working on a text based game. I've tried to make this as organized and professional as possible by trying to follow all conventions.
I have a Map class, shown below:
import logging
#local imports
import Npc
class Map:
def __init__(self, name, npcs = []):
self.name = name
connections = []
if all(isinstance(item, Npc) for item in npcs):
self.npcs = npcs
else:
raise Exception("An NPC was not an instance of NPC")
def addConnection(self, connection):
if(connection == self):
return
self.name = connection.name
self.connections.append(connection)
My Main class creates two instances of these maps named forest, and village.
The point of this code is to add village into the connections array of forest:
village = Map("Village")
forest = Map("Forest")
forest.addConnection(village)
It seems simple enough. But for some reason, when forest.addConnection(village) is run, or even if i do forest.connections.append(village), the Map instance "village" gets added to the connections array of both forest, and village.
According to the debugger, after forest.addConnection(village) is run,
my two objects look as shown:
village (Map)
|------> name="village"
|------> connections = [village]
forest (Map)
|------> name="forest"
|------> connections = [village]
Why is this happening? Nowhere in my code do I add anything to village's connections array. Is there something about object oriented programming in Python I'm not understanding? Should I make village and forest classes that inherit/extend the Map class?
Thanks in advance for all the help.
Try to avoid call a constructor as default argument of a function.
This is the cause of your issue.
Exemple :
>>> class Map():
... def __init__(self, a=list()): # do __init__(self, a=[]) produce same result
... print(a)
... a.append("hello")
...
>>> b = Map()
[]
>>> b = Map()
['hello']
>>> b = Map()
['hello', 'hello']
>>> b = Map()
['hello', 'hello', 'hello']
>>> b = Map()
['hello', 'hello', 'hello', 'hello']
So insead of doing :
def __init__(self, name, npcs = []):
self.name = name
...
do
def __init__(self, name, npcs = None):
if npcs is None:
npcs = []
self.name = name
...
Found the issue. #iElden got me looking in the right place.
In the constructor, I set connections = [], not self.connections = [].
Thanks for the responses!

Why does this print the memory location of an object rather than what I want?

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

How to overload __init__ method based on argument type?

Let's say I have a class that has a member called data which is a list.
I want to be able to initialize the class with, for example, a filename (which contains data to initialize the list) or with an actual list.
What's your technique for doing this?
Do you just check the type by looking at __class__?
Is there some trick I might be missing?
I'm used to C++ where overloading by argument type is easy.
A much neater way to get 'alternate constructors' is to use classmethods. For instance:
>>> class MyData:
... def __init__(self, data):
... "Initialize MyData from a sequence"
... self.data = data
...
... #classmethod
... def fromfilename(cls, filename):
... "Initialize MyData from a file"
... data = open(filename).readlines()
... return cls(data)
...
... #classmethod
... def fromdict(cls, datadict):
... "Initialize MyData from a dict's items"
... return cls(datadict.items())
...
>>> MyData([1, 2, 3]).data
[1, 2, 3]
>>> MyData.fromfilename("/tmp/foobar").data
['foo\n', 'bar\n', 'baz\n']
>>> MyData.fromdict({"spam": "ham"}).data
[('spam', 'ham')]
The reason it's neater is that there is no doubt about what type is expected, and you aren't forced to guess at what the caller intended for you to do with the datatype it gave you. The problem with isinstance(x, basestring) is that there is no way for the caller to tell you, for instance, that even though the type is not a basestring, you should treat it as a string (and not another sequence.) And perhaps the caller would like to use the same type for different purposes, sometimes as a single item, and sometimes as a sequence of items. Being explicit takes all doubt away and leads to more robust and clearer code.
Excellent question. I've tackled this problem as well, and while I agree that "factories" (class-method constructors) are a good method, I would like to suggest another, which I've also found very useful:
Here's a sample (this is a read method and not a constructor, but the idea is the same):
def read(self, str=None, filename=None, addr=0):
""" Read binary data and return a store object. The data
store is also saved in the interal 'data' attribute.
The data can either be taken from a string (str
argument) or a file (provide a filename, which will
be read in binary mode). If both are provided, the str
will be used. If neither is provided, an ArgumentError
is raised.
"""
if str is None:
if filename is None:
raise ArgumentError('Please supply a string or a filename')
file = open(filename, 'rb')
str = file.read()
file.close()
...
... # rest of code
The key idea is here is using Python's excellent support for named arguments to implement this. Now, if I want to read the data from a file, I say:
obj.read(filename="blob.txt")
And to read it from a string, I say:
obj.read(str="\x34\x55")
This way the user has just a single method to call. Handling it inside, as you saw, is not overly complex
with python3, you can use Implementing Multiple Dispatch with Function Annotations as Python Cookbook wrote:
import time
class Date(metaclass=MultipleMeta):
def __init__(self, year:int, month:int, day:int):
self.year = year
self.month = month
self.day = day
def __init__(self):
t = time.localtime()
self.__init__(t.tm_year, t.tm_mon, t.tm_mday)
and it works like:
>>> d = Date(2012, 12, 21)
>>> d.year
2012
>>> e = Date()
>>> e.year
2018
Quick and dirty fix
class MyData:
def __init__(string=None,list=None):
if string is not None:
#do stuff
elif list is not None:
#do other stuff
else:
#make data empty
Then you can call it with
MyData(astring)
MyData(None, alist)
MyData()
A better way would be to use isinstance and type conversion. If I'm understanding you right, you want this:
def __init__ (self, filename):
if isinstance (filename, basestring):
# filename is a string
else:
# try to convert to a list
self.path = list (filename)
You should use isinstance
isinstance(...)
isinstance(object, class-or-type-or-tuple) -> bool
Return whether an object is an instance of a class or of a subclass thereof.
With a type as second argument, return whether that is the object's type.
The form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for
isinstance(x, A) or isinstance(x, B) or ... (etc.).
You probably want the isinstance builtin function:
self.data = data if isinstance(data, list) else self.parse(data)
OK, great. I just tossed together this example with a tuple, not a filename, but that's easy. Thanks all.
class MyData:
def __init__(self, data):
self.myList = []
if isinstance(data, tuple):
for i in data:
self.myList.append(i)
else:
self.myList = data
def GetData(self):
print self.myList
a = [1,2]
b = (2,3)
c = MyData(a)
d = MyData(b)
c.GetData()
d.GetData()
[1, 2]
[2, 3]
My preferred solution is:
class MyClass:
_data = []
__init__(self,data=None):
# do init stuff
if not data: return
self._data = list(data) # list() copies the list, instead of pointing to it.
Then invoke it with either MyClass() or MyClass([1,2,3]).
Hope that helps. Happy Coding!
Why don't you go even more pythonic?
class AutoList:
def __init__(self, inp):
try: ## Assume an opened-file...
self.data = inp.read()
except AttributeError:
try: ## Assume an existent filename...
with open(inp, 'r') as fd:
self.data = fd.read()
except:
self.data = inp ## Who cares what that might be?

Categories

Resources