Class design: Last Modified - python

My class:
class ManagementReview:
"""Class describing ManagementReview Object.
"""
# Class attributes
id = 0
Title = 'New Management Review Object'
fiscal_year = ''
region = ''
review_date = ''
date_completed = ''
prepared_by = ''
__goals = [] # List of <ManagementReviewGoals>.
__objectives = [] # List of <ManagementReviewObjetives>.
__actions = [] # List of <ManagementReviewActions>.
__deliverables = [] # List of <ManagementReviewDeliverable>.
__issues = [] # List of <ManagementReviewIssue>.
__created = ''
__created_by = ''
__modified = ''
__modified_by = ''
The __modified attribute is a datetime string in isoformat. I want that attribute to be automatically to be upated to datetime.now().isoformat() every time one of the other attributes is updated. For each of the other attributes I have a setter like:
def setObjectives(self,objectives):
mro = ManagementReviewObjective(args)
self.__objectives.append(mro)
So, is there an easier way to than to add a line like:
self.__modified = datetime.now().isoformat()
to every setter?
Thanks! :)

To update __modified when instance attributes are modified (as in your example of self.__objectives), you could override __setattr__.
For example, you could add this to your class:
def __setattr__(self, name, value):
# set the value like usual and then update the modified attribute too
self.__dict__[name] = value
self.__dict__['__modified'] = datetime.now().isoformat()

Perhaps adding a decorator before each setter?

If you have a method that commits the changes made to these attributes to a database (like a save() method or update_record() method. Something like that), you could just append the
self.__modified = datetime.now().isoformat()
just before its all committed, since thats the only time it really matters anyway.

Related

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.

Set a nested object property from a dot notation string in python

I’m writing a function that takes in a parent object data and a string inputString that may or may not include dot notation to represent nested objects (i.e. ‘nestedObject.itemA). The function should set the inputString attribute of data to a random string. If the string inputString is a nested object, the function should set the nested object’s value to be a random string. I can’t figure out how to handle this all in a for-loop. I want to do something like this:
split_objects = value.split(“.”)
for item in split_objects:
data.__setattr__(item, get_random_string())
However, in the case of nested objects, the above would set the nested object to be a random string, instead of the field inside. Would someone be able to help me with the syntax to handle both cases? Thanks in advance…
You need to get a reference to data.nestedObject before you can use setattr to change data.nestedObject.itemA.
prefix, suffix = value.rsplit(".",1)
# now prefix is nestedOjbect and suffix is itemA
ref = getattr(data,prefix)
setattr(ref,suffix,get_random_string())
You need to get the reference as many times as there are dots in inputString. So, if you have an arbitrarily deeply nested structure in data
value = "nestedObject.nestedObject2.nestedObject3.itemA"
path, attribute = value.rsplit(".",1)
path = path.split(".")
ref = data
while path:
element, path = path[0], path[1:]
ref = getattr(ref, element)
setattr(ref, attribute, get_random_string())
Here is some example code I to demo a "setField" function I wrote that similar to what you are looking for:
def setField(obj, fieldPath, value):
fields = fieldPath.split(".")
cur = obj
# use all but the last field to traverse the objects
for field in fields[:-1]:
cur = getattr(cur, field)
# use the last field as the property within the object to be overwritten (not traversed)
setattr(cur, fields[-1], value)
# USE CASE EXAMPLE:
class PrintBase:
def dump(self, level=0):
for key, value in vars(self).iteritems():
print " "*(level*4) + key + ":", value
if isinstance(value, PrintBase):
value.dump(level+1)
class BottomObject(PrintBase):
def __init__(self):
self.fieldZ = 'bottomX'
class MiddleObject(PrintBase):
def __init__(self):
self.fieldX = 'middleQ'
self.fieldY = BottomObject()
class TopObject(PrintBase):
def __init__(self):
self.fieldA = 'topA'
self.fieldB = MiddleObject()
top_obj = TopObject()
print "=== BEFORE ==="
top_obj.dump()
print "=== AFTER ==="
setField(top_obj, 'fieldB.fieldY.fieldZ', '!!!! test value !!!!')
top_obj.dump()
And here is the example output:
=== BEFORE ===
fieldB: <__main__.MiddleObject instance at 0x7f5eb1cc6b48>
fieldX: middleQ
fieldY: <__main__.BottomObject instance at 0x7f5eb1cc6b90>
fieldZ: bottomX
fieldA: topA
=== AFTER ===
fieldB: <__main__.MiddleObject instance at 0x7f5eb1cc6b48>
fieldX: middleQ
fieldY: <__main__.BottomObject instance at 0x7f5eb1cc6b90>
fieldZ: !!!! test value !!!!
fieldA: topA

Django-tables2: Provide a list of dictionary, how to generate a column for each dictionary entry

I know if we have a model class, we can make a generate table and use:
class Meta:
model = MyModel
To display every field.
Now say if I have a list of dictionaries, instead of model, is there a similar way to do so?
(Since there are so many different dictionaries, which might be dynamically created, I don't wanna create a customized one each time :-))
You can create your own class that inherits from Table and define the fields you want there.
class JsonTable(Table):
json_key_1 = Column()
json_key_2 = Column()
Also django tables2 have a fields attribute but you can't use it if your data is an array of dicts.
I've been doing this too, here's a rough sketch.
Piggy-backing on top of django_tables2 is miles ahead of rolling your own!
Plus, I hook up the results to jquery FooTable plugin.
import django_tables2 as tables
counter = 0
def generate(li_dict):
#unique classname.
global counter
counter += 1
table_classname = "MyTableClass%s" % (counter)
class Meta:
#ahhh... Bootstrap
attrs = {"class": "table table-striped"}
#generate a class dynamically
cls = type(table_classname,(tables.Table,),dict(Meta=Meta))
#grab the first dict's keys
li = li_dict[0].keys()
for colname in li:
column = tables.Column()
cls.base_columns[colname] = column
return cls
#now, to make use of it...
li_dict = [dict(a=11,b=12,c=13),dict(a=21,b=22,c=23)]
cls = generate(li_dict)
table = cls(li_dict)
# below didn't work, wanted a whole bunch of django setup done first.
# but I fairly confident it would...
print table.as_html()
>>>django.core.exceptions.ImproperlyConfigured: {% querystring %} requires django.core.context_processors.request to be in your settings.TEMPLATE_CONTEXT_PROCESSORS in order for the included template tags to function correctly.
#this did...
print "%s" % table
>>><django_tables2.tables.MyTableClass1 object at 0x1070e1090>
i am sorry for poor english :), but a think which can help, actually with this we can transforme a numpy (matrix) in a generic django table2. By the way, thanks Pyeret for your help.
def convert_array_list_dict(arr):
_list = []
for i in xrange(0, arr.shape[0]):
_list.append(dict(enumerate(arr[i,:])))
for i in xrange(0,len(_list)):
for key in _list[i].keys():
_list[i]["col_" + str(key)] = _list[i].pop(key)
return _list`
This function above convert numpy array to list of dict
counter = 0
def list_dict(dict_):
global counter
counter += 1
table_classname = "MyTableClass%s" % (counter)
class Meta:
attrs = {"class": "paleblue", 'width': '150%'}
cls = type(table_classname, (tables.Table,), dict(Meta=Meta))
list_ = dict_[0].keys()
for colname in list_:
column = tables.Column()
cls.base_columns[colname] = column
return cls
This code make a generic table...and
t = np.loadtxt(doc.document)
tab = convert_array_list_dict(t)
table = list_dict(tab)
table_content = table(tab)
RequestConfig(request, paginate={'per_page': 30}).configure(table_content)
return render(request,'app/snippets/upload_file.html',{'document':document,'table_content':table_content})
Above we can see how use all code...

Method changes all instances of class inside list

In my script I have these classes:
class action:
def __init__(self,ac_type,ac_date):
self.ac_type = ac_type
self.ac_date = ac_date
class user:
actions = []
def __init__(self,i_id):
self.ivi_id = i_id
def add(self,act):
self.actions.append(act)
def get_len(self):
return len(self.actions)
I want to create list of "user" elements and add to some of theme actions. I do this in the following way:
for i in range(len(data_queue)):
ind = users_id.index(data_queue[i].i_id);
act = action(0,data_queue[i].added)
users[ind].add(act)
But after running this I see that every action from data_queue was added to every user from users. This is wrong! What shall I change?
The code is using class attribute which is shared by all instances of the class and the class itself.
Use an instance attribute instead:
class user:
def __init__(self,i_id):
self.ivi_id = i_id
self.actions = []
def add(self,act):
self.actions.append(act)
def get_len(self):
return len(self.actions)
BTW, the code is using an index to iterate the sequence data_queue. Just iterate the sequence unless you really need the index.
for queue in data_queue:
ind = users_id.index(queue.i_id)
act = action(0, queue.added)
users[ind].add(act)

Reusing Python object to add different data to list of objects

Alright, here goes: I have a custom class that I've created to store multiple fields in columnar data as I iterate over the contents of a file line-by-line. I'm wanting to reuse one object each time, adding items to its fields one at a time and then adding this object to a list before running a method that resets all the fields to an empty string. I'll show you a simplified version of what my code is doing.
class MyClass:
firstName = ""
middleName = ""
lastName = ""
def clearAllFields(self):
self.firstName = ""
self.middleName = ""
self.lastName = ""
myClassImpl = MyClass()
nameData = []
for current in open('nameList.csv','r'):
current = current.split(',')
myClassImpl.firstName = current[0]
myClassImpl.middleName = current[1]
myClassImpl.laststName = current[2]
nameData.append(myClassImpl)
myClassImpl.clearAllFields()
for item in nameData:
print item.firstName
Problem is, the output for this code is the first name for the last person in the list, repeated once for every line in the file. What am I doing wrong?
You cannot re-use an instance like this; you are modifying the same object over and over again. nameData.append(myClassImpl) adds a reference to the list, not a copy. In the end, you have a list of references all pointing to the same single copy, which will only show the very last change you made.
Create a new instance for every row instead:
class MyClass:
def __init__(self, first, middle, last):
self.firstName = first
self.middleName = middle
self.lastName = last
for current in open('nameList.csv','r'):
current = current.split(',')
myClassImple = MyClass(*current)
nameData.append(myClassImpl)
or use a collections.namedtuple() class.
Even better, use the csv module to read the CSV data instead of parsing it yourself:
import csv
from collections import namedtuple
MyClass = namedtuple('MyClass', 'firstName middleName lastName')
with open('nameList.csv','r') as infh:
nameData = [MyClass(*row) for row in csv.reader(infh)]

Categories