Create new instances with a condition - python

I am trying to create instances from a dataset only if they have new espn_player_id. If there is a row that has already created as an instance, I just want to execute class method.
td means total data and it is OrderedDict.
However, when it comes to else block, I keep getting an error,
'str' object has no attribute 'num_games'
How can I make td[i]['espn_player_id'] be recognized as a name of existing instance to execute num_games?
class Player:
QBR_h = None
QBR_l = 0
QBR_a = 0
ap = 0
ng = 0
def __init__(self,pid,fname,lname):
self.pid = pid
self.fname = fname
self.lname = lname
print(self.fname, self.lname,'Constructed')
def num_games(self):
self.ng = self.ng + 1
print(self.fname,self.lname,'number of games:',self.ng)
def max_QBR(self):
if self.QBR_h == None or self.QBR_h < td[i]['total_QBR']:
self.QBR_h = td[i]['total_QBR']
print(self.QBR_h)
def asdict(self):
return {'fname':self.fname, 'ng':self.ng}
lst_pid = list()
for i in range(len(td)):
print(td[i]['espn_player_id'])
if td[i]['espn_player_id'] not in lst_pid:
lst_pid.append(td[i]['espn_player_id'])
print(lst_pid)
td[i]['espn_player_id'] = Player(td[i]['espn_player_id'], td[i]['first_name'],td[i]['last_name'])
td[i]['espn_player_id'].num_games()
td[i]['espn_player_id'].max_QBR()
else:
td[i]['espn_player_id'].num_games()
td[i]['espn_player_id'].max_QBR()

Related

Class object not returning value

class Friend:
all = []
def __init__(self):
self.__fname = None
self.__lname = None
self.__fid = None
#property
def fname(self):
return self.__fname
#fname.setter
def fname(self, value):
self.__fname = value
#property
def lname(self):
return self.__lname
#lname.setter
def lname(self, value):
self.__lname = value
#property
def fid(self):
return self.__fid
#fid.setter
def fid(self, value):
self.__fid = value
#DB Class
class db_friend()
def db_load_friend(self, obj, fname,lname):
obj.fname = fname
obj.lname = lname
obj.fid = "XYZ"
obj.all.append(obj)
# function that acts on the friend class
def manage_friend():
fname = "Joe"
lname = "Root"
objfriend = Friend()
db_friend.db_load_friend(objfriend, fname,lname)
print (objfriend.fname) # this is not working
print (objfriend.fid) #this is not working
for user in objfriend.all:
print (objfriend.fid) #this is working
Both objfriend.fname and objfriend.fid is printing no value. I am trying to load the objfriend object by passing to the db_load_friend method of the db class. I am able to see the values if I loop through the "all" variable. May I know why this is not working or using the static variable "all" is the only way to do it?
You need to create an instance of db_friend so you can call the db_load_friend() method:
def manage_friend():
fname = "Joe"
lname = "Root"
objfriend = Friend()
objdbfriend = db_friend()
objdbfriend.db_load_friend(objfriend, fname,lname)
print (objfriend.fname)
print (objfriend.fid)
for user in objfriend.all:
print (objfriend.fid) #this is working
Or, since db_load_friend() doesn't need to use self, you could make it a static method.
class db_friend()
#staticmethod
def db_load_friend(obj, fname,lname):
obj.fname = fname
obj.lname = lname
obj.fid = "XYZ"
obj.all.append(obj)

Items appended to object's list append to every object's list

I'm building a simple blockchain/cryptocurrency to learn about python and blockchain programming.
I've run into an issue regarding appending transaction objects to the list variable 'transactions' in my Block objects.
For whatever reason, when adding a transaction to a block, it is added to every block on the chain.
I have uploaded my code to a github repo:
The project consists of 3 class files: Blockchain.py, Block.py & Transaction.py
I also have a testing file 'test1.py' which reproduces the error.
https://github.com/swooperior/blockchain-py
I suspect the issue is in the Block class file:
#Not intended behaviour. addTransaction seems to add to every block in self.chain
from datetime import datetime
import hashlib
class Block:
hash = ''
txIndex = 0
transactions = []
timeStamp = ''
previous_hash = ''
nonce = 0
def calculateHash(self):
self.hash = str(hashlib.sha256(repr([self.transactions,self.previous_hash,self.nonce]).encode('utf-8')).hexdigest())
def getHash(self):
return self.hash
def addTransaction(self,tx):
#Validate transaction, then pass to transactions list
tx.id = self.txIndex
self.transactions.append(tx)
self.txIndex += 1
def printDetails(self):
print('Block Hash: '+self.getHash())
print('Nonce: '+str(self.nonce))
print('Created: '+ str(datetime.fromtimestamp(self.timeStamp)))
print('Prev_hash: '+self.previous_hash)
print('Transactions ('+str(len(self.transactions))+'):')
self.printTransactions()
def printTransactions(self):
c = 1
for tx in self.transactions:
print('Transaction:'+ str(c))
tx.printDetails()
c += 1
def __init__(self,txlist=[],prev_hash=''):
self.txIndex = 0
self.previous_hash = prev_hash
for tx in txlist:
self.addTransaction(tx)
self.timeStamp = datetime.timestamp(datetime.now())
self.nonce = 1
self.calculateHash()
#print(self.printDetails())
The transactions attribute is a class attribute for all instances of the class. When you instantiate the class, you should create an instance variable instead. You also shouldn’t use a mutable default argument.
class Block:
...
def __init__(self, txlist=None, prev_hash=''):
self.transactions = []
txlist = txlist or []
self.previous_hash = prev_hash
for tx in txlist:
self.addTransaction(tx)
self.timeStamp = datetime.timestamp(datetime.now())
self.nonce = 1
self.calculateHash()
Function defaults are only evaluated once so each instance uses the same default argument unless you give it another one. This only happens to mutable objects as re-assigning them doesn’t copy them.

Return class instance instead of creating a new one if already existing

I defined a class named Experiment for the results of some lab experiments I am conducting. The idea was to create a sort of database: if I add an experiment, this will be pickled to a db before at exit and reloaded (and added to the class registry) at startup.
My class definition is:
class IterRegistry(type):
def __iter__(cls):
return iter(cls._registry)
class Experiment(metaclass=IterRegistry):
_registry = []
counter = 0
def __init__(self, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
hashdat = fn.hashfile(pathresult)
hashpro = fn.hashfile(pathprotocol)
chk = fn.checkhash(hashdat)
if chk:
raise RuntimeError("The same experiment has already been added")
self._registry.append(self)
self.name = name
[...]
While fn.checkhash is a function that checks the hashes of the files containing the results:
def checkhash(hashdat):
for exp in cl.Experiment:
if exp.hashdat == hashdat:
return exp
return False
So that if I add a previously added experiment, this won't be overwritten.
Is it possible to somehow return the existing instance if already existant instead of raising an error? (I know in __init__ block it is not possible)
You can use __new__ if you want to customize the creation instead of just initializing in newly created object:
class Experiment(metaclass=IterRegistry):
_registry = []
counter = 0
def __new__(cls, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
hashdat = fn.hashfile(pathresult)
hashpro = fn.hashfile(pathprotocol)
chk = fn.checkhash(hashdat)
if chk: # already added, just return previous instance
return chk
self = object.__new__(cls) # create a new uninitialized instance
self._registry.append(self) # register and initialize it
self.name = name
[...]
return self # return the new registered instance
Try to do it this way (very simplified example):
class A:
registry = {}
def __init__(self, x):
self.x = x
#classmethod
def create_item(cls, x):
try:
return cls.registry[x]
except KeyError:
new_item = cls(x)
cls.registry[x] = new_item
return new_item
A.create_item(1)
A.create_item(2)
A.create_item(2) # doesn't add new item, but returns already existing one
After four years of the question, I got here and Serge Ballesta's answer helped me. I created this example with an easier syntax.
If base is None, it will always return the first object created.
class MyClass:
instances = []
def __new__(cls, base=None):
if len(MyClass.instances) == 0:
self = object.__new__(cls)
MyClass.instances.append(self)
if base is None:
return MyClass.instances[0]
else:
self = object.__new__(cls)
MyClass.instances.append(self)
# self.__init__(base)
return self
def __init__(self, base=None):
print("Received base = %s " % str(base))
print("Number of instances = %d" % len(self.instances))
self.base = base
R1 = MyClass("apple")
R2 = MyClass()
R3 = MyClass("banana")
R4 = MyClass()
R5 = MyClass("apple")
print(id(R1), R1.base)
print(id(R2), R2.base)
print(id(R3), R3.base)
print(id(R4), R4.base)
print(id(R5), R5.base)
print("R2 == R4 ? %s" % (R2 == R4))
print("R1 == R5 ? %s" % (R1 == R5))
It gives us the result
Received base = apple
Number of instances = 2
Received base = None
Number of instances = 2
Received base = banana
Number of instances = 3
Received base = None
Number of instances = 3
Received base = apple
Number of instances = 4
2167043940208 apple
2167043940256 None
2167043939968 banana
2167043940256 None
2167043939872 apple
R2 == R4 ? True
R1 == R5 ? False
Is nice to know that __init__ will be always called before the return of the __new__, even if you don't call it (in commented part) or you return an object that already exists.

Passing Data Between Classes in Python

I am attempting a practice task I found in an old programming book to increase my knowledge of classes in Python. The task is to create a program which allows a user to set up a series of tests for a school. Each test must contain no more than 10 questions. The task stated that the best way to do this was to use containment, and have the class 'Question' inside the class 'Test'
Basically, I should set up a class called Test which dewfines the basics of the whole test, and then a class called Quesion which sets up the question and passes it back to Test to be included in the array there. I'm having 2 major problems. Firstly, how do I get the setQuestion object in the Question class to pass data in to the Question array in the Test class. Secondly, how do I have the setQuestion object iterate the variable numberofQuestions since that's contained in the Test Class.
Here is the code. Not sure it's clear from the formatting but the Question class is inside the Test class:
class Test(object):
def __init__(self):
self.__testID = 0
self.__maxMarks = 0
self.__questions = []
self.__numberofQuestions = 0
self.__level = ""
self.__dateSet = ""
class Question(object):
def __init__(self):
self.__questionID = 0
self.__questionText = ""
self.__answer = ""
self.__marks = 0
self.__topic = ""
def setQuestion(self, questionID, questionText, answer, marks, topic):
self.__numberofQuestions = self.__numberofQuestions + 1
self.__questionID = self.__questionID
self.__questionText = self.__questionText
self.__answer = self.__answer
self.__marks = self.__marks
self.__topic = self.__topic
This is how I would do that:
class Test(object):
def __init__(self,id,marks):
self.__testID = id
self.__maxMarks = marks
self.__questions = []
self.__numberofQuestions = 0
self.__level = ""
self.__dateSet = ""
def setQuestion(self,question):
self.__numberofQuestions += 1
self.__questions.append(question)
class Question(object):
def __init__(self,id,text,answer,marks,topic):
self.__questionID = id
self.__questionText = text
self.__answer = answer
self.__marks = marks
self.__topic = topic
Now you can put question objects into the __question array of Test like that:
if __name__ == "__main__":
test = Test(1,100)
test.setQuestion(Question(1,"Text","Answer",50,"Topic"))

Python Error: 'finalStore' object has no attribute 'name'

I'm working with Inheritance in python but i'm getting an error i don't know how to fix, 'finalStore' object has no attribute 'marone'. I get this when i try create an object.
from ClassFile import studStore
class finalStore (studStore):
grandAve = 0
numStu = 0
def __init__(self, name, marone, martwo, marthree, marfour, corone, cortwo, corthree, corfour):
studStore.__init__(self, name, marone, martwo, marthree, marfour)
self.corone = corone
self.cortwo = cortwo
self.corthree = corthree
self.corfour = corfour
finalStore.numStu += 1
self.holder = finalStore.numStu
self.average = (marone + martwo + marthree + marfour)/4
finalStore.grandAve += self.average
self.storit = finalStore.grandAve
My initializing for the child class
class studStore:
def __init__(self, name, marone, martwo, marthree, marfour):
self.newname = name
self.Ave = 0
self.marone = marone
self.martwo = martwo
self.marthree = marthree
self.marfour = marfour
And the initializing for the parent class. My main line is just a loop where i create multiple objects for but it errors on this line:
listIn.append(finalStore(name, gradeone, gradetwo, gradethree, gradefour, courseOne, courseTwo, courseThree, courseFour))
I'm not sure what the error is but I have a similar program that works, I'm just not using the from * import *
I'm outputting it like this
for i in range (0,len(listIn)):
print(str(listIn[i].returnName()).ljust(20," "), end = " ")
print(str(listIn[i].returnOne()).ljust(20, " "))
print(str(listIn[i].returnTwo()).ljust(20, " "))
print(str(listIn[i].returnThree()).ljust(20, " "))
print(str(listIn[i].returnFour()).ljust(20, " "))
Your call to the super class's init function is incorrect. Here is how you should do it:
class finalStore(studStore):
def __init__(self, name, ...):
super(finalStore, self).__init__(name, marone, ...)

Categories