I am working on creating a python module for getting stocks data.
I have a dictionary:
{'StockSymbol': 'AMD', 'LastTradeTime': '4:00PM EST', 'ChangePercent': '+0.58', 'ID': '327', 'LastTradeDateTimeLong': 'Mar 10, 4:00PM EST', 'Index': 'NASDAQ', 'LastTradeWithCurrency': '13.91', 'LastTradeDateTime': '2017-03-10T16:00:02Z', 'LastTradePrice': '13.91', 'LastTradeSize': '0', 'PreviousClosePrice': '13.33'}
Currently I have 11 methods such as:
class Stock(object):
def getSymbol():
return self.data['StockSymbol']
def getLastTradeTime():
return self.data['LastTradeTime']
........
I use it as:
google = Stock('GOOG')
print(google.getLastTradeTime()) //4:00PM EST
My question is, Is it possible to generate these methods dynamically?
So I could do google.getLastTradeSize() etc without defining them.
Here is a Python fiddle: https://repl.it/GSG1
In Python there's a design pattern called bunch, it works like this, I believe it can solve your problem:
class Bunch(dict):
def __init__(self, *args, **kwargs):
super(Bunch, self).__init__(*args, **kwargs)
self.__dict__ = self
def __getattribute__(self, item):
try:
return object.__getattribute__(self, item)
except:
return None
my_dict = {'StockSymbol': 'AMD', 'LastTradeTime': '4:00PM EST', 'ChangePercent': '+0.58', 'ID': '327',
'LastTradeDateTimeLong': 'Mar 10, 4:00PM EST', 'Index': 'NASDAQ', 'LastTradeWithCurrency': '13.91',
'LastTradeDateTime': '2017-03-10T16:00:02Z', 'LastTradePrice': '13.91', 'LastTradeSize': '0',
'PreviousClosePrice': '13.33'}
obj = Bunch(**my_dict)
print obj.StockSymbol
print obj.LastTradeTime
print obj.key_not_exist
And we get:
AMD
4:00PM EST
None
So you don't have to define your so-called gettter method, like what you do in Java/C++;
PS: in a real project, you can also inherit from this Bunch class.
===== Another optional ======
you can use pythonic-toolbox, a 3rd party lib maintained by me, that contains many useful tools, demos.
For your case, I think DictObj is a good choicehere in this lib.
my_dict = {'StockSymbol': 'AMD', 'LastTradeTime': '4:00PM EST', 'ChangePercent': '+0.58', 'ID': '327',
'LastTradeDateTimeLong': 'Mar 10, 4:00PM EST', 'Index': 'NASDAQ', 'LastTradeWithCurrency': '13.91',
'LastTradeDateTime': '2017-03-10T16:00:02Z', 'LastTradePrice': '13.91', 'LastTradeSize': '0',
'PreviousClosePrice': '13.33'}
from pythonic_toolbox.utils.dict_utils import DictObj
obj = DictObj(my_dict)
assert hasattr(obj, 'StockSymbol')
assert obj.StockSymbol == 'AMD'
assert 'StockSymbol' in obj
assert obj.pop('LastTradePrice') == '13.91'
assert 'LastTradePrice' not in obj # 'LastTradePrice' is popped up, so obj don't have attribute LastTradePrice anymore
del obj.LastTradeSize
assert not hasattr(obj, 'LastTradeSize') # besides pop key as a dict, you can also delete it like an attribute
obj.greetings = 'hello world' # assign new key/attribute
assert obj['greetings'] == 'hello world'
If you don't want others to change your DictObj attributes(modify, del, add), you can also use FinalDictObj, by from pythonic_toolbox.utils.dict_utils import FinalDictObj
Related
I'm scraping a website, which returns a dictionary:
person = {'name0':{'first0': 'John', 'last0':'Smith'},
'age0':'10',
'location0':{'city0':'Dublin'}
}
I'm trying to write a function that will return a dictionary {'name':'John', 'age':'10'} when passed the above dictionary.
I want to ideally put a try:... except KeyError around each item since sometimes keys will be missing.
def func(person):
filters = [('age', 'age0'), ('name', ['name0', 'first0'])]
result = {'name': None, 'age': None}
for i in filters:
try:
result[i[0]] = person[i[1]]
except KeyError:
pass
return result
The problem is result[i[0]] = person[i[1]] doesn't work for 'name' since there's two keys that need to be followed sequentially and I don't know how to do that.
I want some way of telling it (in the loop) to go to person['name0']['first0'] (and so on to whatever depth the thing I want is).
I have lots of things to extract, so I'd rather do it in a loop instead of a try..except statement for each variable individually.
In order to follow several key sequentially, you can use get and set the default value to {} (empty dictionary) for the upper levels. Set the default value to None (or whatever suits you) for the last level:
def func(person):
return {'name': person.get('name0', {}).get('first0', None),
'age': person.get('age0', None)}
Best I could manage was using a for loop to iterate through the keys:
person = {'name0':{'first0': 'John', 'last0':'Smith'},
'age0':'10',
'location0':{'city0':'Dublin'}
}
Additionally I used .get(key) rather than try..except as suggested by #wiwi
def func(person):
filters = [('age', ['age0']), ('name', ['name0', 'first0'])]
result = {'name': None, 'age': None}
for filter in filters:
temp = person.copy()
for key in filter[1]:
temp = temp.get(key)
if not temp: # NoneType doesn't have .get method
break
result[filter[0]] = temp
return result
func(person) then returns {'name': 'John', 'age': '10'}.
It handles missing input too:
person2 = {'age0':'10',
'location0':{'city0':'Dublin'}}
func(person2) returns {'name': None, 'age': '10'}
You can put the try...except in another loop, if there's a list of keys instead of a single key:
def getNestedVal(obj, kPath:list, defaultVal=None):
if isinstance(kPath, str) or not hasattr(kPath, '__iter__'):
kPath = [kPath] ## if not iterable, wrap as list
for k in kPath:
try: obj = obj[k]
except: return defaultVal
return obj
def func(person):
filters = [('age', 'age0'), ('name', ['name0', 'first0']),#]
('gender', ['gender0'], 'N/A')] # includes default value
return {k[0]: getNestedVal(person, *k[1:3]) for k in filters}
[I added gender just to demonstrate how defaults can also be specified for missing values.]
With this, func(person) should return
{'age': '10', 'name': 'John', 'gender': 'N/A'}
I also have a flattenObj function, a version of which is defined below:
def flattenDict(orig:dict, kList=[], kSep='_', stripNum=True):
if not isinstance(orig, dict): return [(kList, orig)]
tList = []
for k, v in orig.items():
if isinstance(k, str) and stripNum: k = k.strip('0123456789')
tList += flattenDict(v, kList+[str(k)], None)
if not isinstance(kSep, str): return tList
return {kSep.join(kl): v for kl,v in tList}
[I added stripNum just to get rid of the 0s in your keys...]
flattenDict(person) should return
{'name_first': 'John', 'name_last': 'Smith', 'age': '10', 'location_city': 'Dublin'}
I have a enum OsTypeEnum:
class OsTypeEnum(Enum):
WINDOWS = 100
LINUX = 200
MAC = 300
ANDROID = 400
IOS = 500
#classmethod
def get_list(cls):
ret = []
for e in cls:
ret.append({'name': e.name, 'value': e.value})
return ret
I need to hide ANDROID and IOS from calling the get_list function, but don't want to remove them from the OsTypeEnum.
Rather than hard-code the list of members to exclude, I would make that information part of each member instead. I'll show code using the aenum library1, but it can be done using the stdlib version, just more verbosely.
from aenum import Enum
class OsTypeEnum(Enum):
#
_init_ = 'value type'
#
WINDOWS = 100, 'pc'
LINUX = 200, 'pc'
MAC = 300, 'pc'
ANDROID = 400, 'mobile'
IOS = 500, 'mobile'
#
#classmethod
def get_pc_list(cls):
ret = []
for e in cls:
if e.type == 'pc':
ret.append({'name': e.name, 'value': e.value})
return ret
#
#classmethod
def get_mobile_list(cls):
ret = []
for e in cls:
if e.type == 'mobile':
ret.append({'name': e.name, 'value': e.value})
return ret
By storing that extra piece of information on the member, you are more easily able to get your original list, plus other lists.
In use, it looks like:
>>> OsTypeEnum.get_pc_list()
[{'name': 'WINDOWS', 'value': 100}, {'name': 'LINUX', 'value': 200}, {'name': 'MAC', 'value': 300}]
>>> OsTypeEnum.get_mobile_list()
[{'name': 'ANDROID', 'value': 400}, {'name': 'IOS', 'value': 500}]
1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.
You can create excluded enums list
class OsTypeEnum(Enum):
WINDOWS = 100
LINUX = 200
MAC = 300
ANDROID = 400
IOS = 500
#classmethod
def get_list(cls):
ret = []
for e in cls:
if e not in cls.__get_excluded():
ret.append({'name': e.name, 'value': e.value})
return ret
#classmethod
def __get_excluded(cls):
return [cls.ANDROID, cls.IOS]
This seems like a good candidate for an if statement. if the enum is not ANDROID or IOS, then add it to the return value.
I have an use case where multi-inheritance seems the right way to go. But it implies sharing attributes between "sibling" classes, attributes that are initialized on other classes (so somehow unknown for them).
I'm asking if this below is a "right" and "pythonic" model, or should I better go with a dertivated-classes model.
Let's say we want to develop different deliverers, which will take some source data, apply some format to it, and send it through some channel. And this three parts (data - format - send) can be customizable for each case.
First, come code to make the examples below working:
import sys
PY3 = not sys.version_info < (3,)
from string import Template
import csv, io, smtplib, requests, os
def read_test_movies(year_from, year_to, genre= None):
TEST_MOVIES= [
{'year': 1971, 'release': '01/01/1971', 'genre': 'thriller', 'title': 'Play Misty for Me'},
{'year': 1973, 'release': '02/02/1973', 'genre': 'romantic', 'title': 'Breezy'},
{'year': 1976, 'release': '03/03/1976', 'genre': 'western', 'title': 'The Outlaw'},
{'year': 1986, 'release': '04/04/1986', 'genre': 'war', 'title': 'Heartbreak'},
{'year': 1988, 'release': '05/05/1988', 'genre': 'music', 'title': 'Bird'},
{'year': 1992, 'release': '06/06/1992', 'genre': 'western', 'title': 'Unforgiven'},
{'year': 1995, 'release': '07/07/1995', 'genre': 'romantic', 'title': 'The Bridges of Madison County'},
{'year': 2000, 'release': '08/08/2000', 'genre': 'space', 'title': 'Space Cowboys'},
{'year': 2003, 'release': '09/09/2003', 'genre': 'trhiller', 'title': 'Mystic River'},
{'year': 2004, 'release': '10/10/2004', 'genre': 'sports', 'title': 'Million Dollar Baby'},
{'year': 2006, 'release': '11/11/2006', 'genre': 'war', 'title': 'Flags of Our Fathers'},
{'year': 2006, 'release': '12/12/2006', 'genre': 'war', 'title': 'Letters from Iwo Jima'},
{'year': 2008, 'release': '13/11/2008', 'genre': 'drama', 'title': 'Changeling'},
{'year': 2008, 'release': '14/10/2008', 'genre': 'drama', 'title': 'Gran Torino'},
{'year': 2009, 'release': '15/09/2009', 'genre': 'sports', 'title': 'Invictus'},
{'year': 2010, 'release': '16/08/2010', 'genre': 'drama', 'title': 'Hereafter'},
{'year': 2011, 'release': '17/07/2011', 'genre': 'drama', 'title': 'J. Edgar'},
{'year': 2014, 'release': '18/06/2014', 'genre': 'war', 'title': 'American Sniper'},
{'year': 2016, 'release': '19/05/2016', 'genre': 'drama', 'title': 'Sully'}
]
out= []
for m in TEST_MOVIES:
if year_from <= m['year'] and m['year'] <= year_to:
if genre is None or (genre is not None and genre == m['genre']):
out.append(m)
return out
Being this three parts (data - format - send) so distinguishable, we would start with these interface-like classes (I guess abc could be used too):
class ITheData(object):
def __init__(self, year_from, year_to, genre= None):
self.year_from= year_from
self.year_to = year_to
self.genre = genre
def readMovies(self):
raise NotImplementedError('%s.readMovies() must be implemented' % self.__class__.__name__)
class ITheFormat(object):
def filename(self):
raise NotImplementedError('%s.filename() must be implemented' % self.__class__.__name__)
def make(self):
raise NotImplementedError('%s.make() must be implemented' % self.__class__.__name__)
class ITheSend(object):
def send(self):
raise NotImplementedError('%s.send() must be implemented' % self.__class__.__name__)
For each custom deliver, we will subclass the three of them, and put them together in a class like:
class ITheDeliverer(ITheData, ITheFormat, ITheSend):
def deliver(self):
raise NotImplementedError('%s.deliver() must be implemented' % self.__class__.__name__)
So, we could have two different data sources. Apart from source, they may differ on post-processing actions. Although for simplicity I'm just doing a self.readMovies() all over the place, it could be some other custom method on the subclass.
class TheIMDBData(ITheData):
def readMovies(self):
# movies = some_read_from_IMDB(self.genre, self.year_from, self.year_to)
movies= read_test_movies(self.year_from, self.year_to, self.genre)
return movies
class TheTMDbData(ITheData):
def readMovies(self):
# movies = some_read_from_TMDb(self.genre, self.year_from, self.year_to)
movies= read_test_movies(self.year_from, self.year_to, self.genre)
return movies
We could use also two different formats:
class TheTXTFormat(ITheFormat):
def filename(self):
# Here `genre`, `year_from` and `year_to` are unknown
params= {'genre': self.genre, 'year_from': self.year_from, 'year_to': self.year_to}
return Template('movies_of_${genre}_from_${year_from}_to_${year_to}.txt').substitute(**params)
def make(self):
# Here `readMovies()` is unknown
strio = PY3 and io.StringIO() or io.BytesIO()
for movie in self.readMovies():
line= Template('$title, released on $release').substitute(**movie)
line+= '\n'
strio.write(line)
strio.seek(0)
return strio.read()
class TheCSVFormat(ITheFormat):
def filename(self):
# Here `genre`, `year_from` and `year_to` are unknown
params= {'genre': self.genre, 'year_from': self.year_from, 'year_to': self.year_to}
return Template('movies_of_${genre}_from_${year_from}_to_${year_to}.csv').substitute(**params)
def make(self):
# Here `readMovies()` is unknown
strio = PY3 and io.StringIO() or io.BytesIO()
writer = csv.writer(strio, delimiter=';', quotechar='"', quoting=csv.QUOTE_MINIMAL)
header = ('Title', 'Release')
writer.writerow(header)
for movie in self.readMovies():
writer.writerow((movie['title'], movie['release']))
strio.seek(0)
return strio.read()
And two different sending channels:
class TheMailSend(ITheSend):
host = 'localhost'
sender = 'movie#spammer.com'
receivers = ['movie#spammed.com']
def send(self):
# Here `make()` is unknown
print('TheMailSend.send() Sending to %s' % str(self.receivers))
try:
message = self.make() # Format agnostic
smtpObj = smtplib.SMTP(self.host)
smtpObj.sendmail(self.sender, self.receivers, message)
return True, 'ok'
except Exception as ss:
return False, str(ss)
class TheWSSend(ITheSend):
url = 'spammed.com/movies/send'
def send(self):
# Here `make()` is unknown
print('TheWSSend.send() Sending to %s' % str(self.url))
try:
content = self.make() # Format agnostic
s= requests.Session()
response= s.post(url= self.url, data= {'content': content})
s.close()
if response.status_code == 200:
return True, 'ok'
else:
return False, response.status_code
except Exception as ss:
return False, str(ss)
So, we could end with some deliverers like these:
class TheIMDBToTXTFile(ITheDeliverer, TheIMDBData, TheTXTFormat):
def __init__(self, year_from, year_to, genre= None):
TheIMDBData.__init__(self, year_from, year_to, genre)
def deliver(self):
filepath= os.path.join('/tmp', self.filename())
f= open(filepath, 'w')
f.write(self.make())
f.close()
print('TheIMDBToTXTFile.deliver() => Successfully delivered to %s' % str(filepath))
class TheIMDBToWS(ITheDeliverer, TheIMDBData, TheTXTFormat, TheWSSend):
def __init__(self, year_from, year_to, genre=None):
TheIMDBData.__init__(self, year_from, year_to, genre)
def deliver(self):
ok, msg = self.send()
if ok:
print('TheIMDBToWS.deliver() => Successfully delivered!')
else:
print('TheIMDBToWS.deliver() => Error delivering: %s' % str(msg))
class TheTMDbToMail(ITheDeliverer, TheTMDbData, TheCSVFormat, TheMailSend):
def __init__(self, year_from, year_to, genre=None):
TheTMDbData.__init__(self, year_from, year_to, genre)
def deliver(self):
ok, msg= self.send()
if ok:
print('TheTMDbToMail.deliver() => Successfully delivered!')
else:
print('TheTMDbToMail.deliver() => Error delivering: %s' % str(msg))
And they work fine -with obvious connection errors-:
>>> imdbToTxt = TheIMDBToTXTFile(year_from= 2000, year_to= 2010)
>>> imdbToTxt.deliver()
TheIMDBToTXTFile.deliver() => Successfully delivered to /tmp/movies_of_None_from_200_to_2010.txt
>>>
>>> imdbToWs = TheIMDBToWS(year_from= 2000, year_to= 2010)
>>> imdbToWs.deliver()
TheWSSend.send() Sending to http://spammed.com/movies/send?
TheIMDBToWS.deliver() => Error delivering: 405
>>>
>>> tmdbToMail = TheTMDbToMail(year_from= 1980, year_to= 2019, genre= 'war')
>>> tmdbToMail.deliver()
TheMailSend.send() Sending to ['movie#spammed.com']
TheTMDbToMail.deliver() => Error delivering: [Errno 111] Connection refused
But, as commented, some attributes are unknown for some classes, and the linter is -obviously- complaining about it:
Instance of 'TheTXTFormat' has no 'genre' member
Instance of 'TheTXTFormat' has no 'year_from' member
Instance of 'TheTXTFormat' has no 'year_to' member
Instance of 'TheTXTFormat' has no 'readMovies' member
Instance of 'TheCSVFormat' has no 'genre' member
Instance of 'TheCSVFormat' has no 'year_from' member
Instance of 'TheCSVFormat' has no 'year_to' member
Instance of 'TheCSVFormat' has no 'readMovies' member
Instance of 'TheMailSend' has no 'make' member
Instance of 'TheWSSend' has no 'make' member
So, the question remains: is here multi-inheritance a good model?
The alternatives could be: a derivated-classes model, or just independent classes and passing around parameters like data or formatter. But none of them seem so simple as multi-inheritance (although they'd fix linter -and probably conceptual- problems).
I don't think inheritance is a good model here. You have lots of classes and it gets messy.
I think It's ok to implement inheritance for different "flavors" of the same step, of using the "Template pattern" described here.
from abc import ABC, abstractmethod
class ITheSend(ABC):
def run(self) -> None:
"""
The template method defines the skeleton of an algorithm.
"""
self.pre_send_hook()
self.send()
self.post_send_hook()
# These operations have to be implemented in subclasses.
#abstractmethod
def send(self) -> None:
pass
# These are "hooks." Subclasses may override them, but it's not mandatory
# since the hooks already have default (but empty) implementation. Hooks
# provide additional extension points in some crucial places of the
# algorithm.
def pre_send_hook(self) -> None:
pass
def post_send_hook(self) -> None:
pass
class TheMailSend(ITheSend):
host = 'localhost'
sender = 'movie#spammer.com'
receivers = ['movie#spammed.com']
def send(self, message):
print('TheMailSend.send() Sending to %s' % str(self.receivers))
try:
smtpObj = smtplib.SMTP(self.host)
smtpObj.sendmail(self.sender, self.receivers, message)
return True, 'ok'
except Exception as ss:
return False, str(ss)
class TheWSSend(ITheSend):
url = 'spammed.com/movies/send'
def send(self, content):
print('TheWSSend.send() Sending to %s' % str(self.url))
try:
s= requests.Session()
response= s.post(url= self.url, data= {'content': content})
s.close()
if response.status_code == 200:
return True, 'ok'
else:
return False, response.status_code
except Exception as ss:
return False, str(ss)
However, for the full chain I'd explore composition instead of inheritance.
class Chain:
def __init__(self, data, format, send):
self._data = data
self._format = format
self._send = send
def deliver(self):
data = self._data.execute()
format = self._format.execute(data)
send = self._send.execute(format)
the_IMDB_to_TXT_file = Chain(send=ITheDeliverer, data=TheIMDBData, format=TheTXTFormat)
I'm trying to separate various functions in my program to keep things neat. And I'm getting stuck trying to use variables created in one module in another module. I tried using global list_of_names but it wasn't working, and I've read that it's recommended not to do so anyway.
Below is a sample of my code. In my opinion, it doesn't make sense to pass list_of_names as a function argument because there are multiple other variables that I need to do this with, aside from the actual arguments that do get passed.
Unfortunately, even if I were to move read_json into engine.py, I'd still have the same problem in main.py as I need to reference list_of_names there as well.
# main.py:
import json
from engine import create_person
def read_json():
with open('names.json', 'r') as file
data = json.load(file)
return data
list_of_names = read_json()
person1 = create_person()
# engine.py:
from random import choice
def create_person():
name = choice(list_of_names)
new_person = {
'name': name,
# other keys/values created in similar fashion
}
return new_person
EDIT1:
Here's my new code. To me, this doesn't seem efficient to have to build the parameter list and then deconstruct it inside the function. (I know I'm reusing variable names for this example) Then I have to pass some of those parameters to other functions.
# main.py:
import json
from engine import create_person
def read_json():
with open('names.json', 'r') as file
data = json.load(file)
return data
player_id_index = 0
list_of_names = read_json()
person_parameters = [
list_of_names,
dict_of_locations,
player_id_index,
dict_of_occupations,
.
.
.
]
person1, player_id_index = create_person()
# engine.py:
from random import choice
def create_person(person_params):
list_of_names = person_params[0]
dict_of_locations = person_params[1]
player_id_index = person_params[2]
dict_of_occupations = person_params[3]
.
.
.
attr = person_params[n]
name = choice(list_of_names)
location = get_location(dict_of_locations) # a function elsewhere in engine.py
p_id = player_id_index
occupation = get_occupation(dict_of_occupations) # a function elsewhere in engine.py
new_person = {
'name': name,
'hometown': location,
'player id': p_id,
'occupation': occupation,
.
.
.
}
player_id_index += 1
return new_person, player_id_index
In general you should not be relying on shared global state. If you need to share state encapsulate the state in objects or pass as function arguments.
Regarding your specific problem it looks like you want to assemble random dictionaries from a set of options. It could be coded like this:
from random import choice
person_options = {
'name': ['fred', 'mary', 'john', 'sarah', 'abigail', 'steve'],
'health': [6, 8, 12, 15],
'weapon': ['sword', 'bow'],
'armor': ['naked', 'leather', 'iron']
}
def create_person(person_options):
return {k:choice(opts) for k, opts in person_options.items()}
for _ in range(4):
print create_person(person_options)
In action:
>>> for _ in range(4):
... print(create_person(person_options))
...
{'armor': 'naked', 'weapon': 'bow', 'health': 15, 'name': 'steve'}
{'armor': 'iron', 'weapon': 'sword', 'health': 8, 'name': 'fred'}
{'armor': 'iron', 'weapon': 'sword', 'health': 6, 'name': 'john'}
{'armor': 'iron', 'weapon': 'sword', 'health': 12, 'name': 'john'}
Note that a dictionary like {'armor': 'naked', 'weapon': 'bow', 'health': 15, 'name': 'steve'} looks like it might want to be an object. A dictionary is a glob of state without any defined behavior. If you make a class to house this state the class can grow methods that act on that state. Of course, explaining all this could make this answer really really long. For now, just realize that you should move away from having shared state that any old bit of code can mess with. A little bit of discipline on this will make your code much easier to refactor later on.
This addresses your edited question:
from random import choice
from itertools import count
from functools import partial
person_options = {
'name': partial(
choice, ['fred', 'mary', 'john', 'sarah', 'abigail', 'steve']),
'location': partial(
get_location, {'heaven':1, 'hell':2, 'earth':3}),
'player id': count(1).next
}
def create_person(person_options):
return {k:func() for k, func in person_options.items()}
However, we are now way beyond the scope of your original question and getting into specifics that won't be helpful to anyone other than you. Such questions are better asked on Code Review Stack Exchange
I'm new to python programming and I have a (maybe) specific question.
I don't know if python lets you do this, but basically i want to assign a variable to represent a whole block of stuff.
for example i have this:
item.history[len(item.history)] = {'user': item.user,
'email': item.email, 'phone': item.phone,
'status': item.status, 'usage': item.usage,
'checkouttime': item.checkouttime,
'checkintime': item.checkintime,
'timeout': item.checkintime - item.checkouttime}
this is alot of stuff in one spot, and in my program the same chunk of this code (shown below as ShortHis) is repeated around 15 times, with changes to the history key, and different variables instead of checkintime and checkouttime sometimes, so I figured if i did this:
ShortHis = ('user': item.user,
'email': item.email, 'phone': item.phone,
'status': item.status, 'usage': item.usage,
'checkouttime': item.checkouttime,
'checkintime': item.checkintime,)
then this:
item.history[len(item.history)] = {ShortHis
'timeout': item.checkintime - item.checkouttime}
would work and I could save some space, and still be able to edit the key and the checkintime and checkouttime variables, but it does not.
Why doesn't this work and how can I make something like I'm wanting? (If something like that exists)
I'm sorry if I've left out specific terminology for what these things are called, as I said I'm new to python programming.
More Clarity: I want a chunk of stuff, any stuff, regardless of content or length, to be assigned to a variable (or something) that represents that stuff just like it was before, so I can put that variable in the middle of something and it still run as if the original code was there.
It is probably a good idea to just use a function here
def get_short_history(item):
return {
'user': item.user,
'email': item.email,
'phone': item.phone,
'status': item.status,
'usage': item.usage,
'checkouttime': item.checkouttime,
'checkintime': item.checkintime
}
you can then reuse this chunk
items.history.append(get_short_history(item).update({checkintime: 'overwrite'}))
... a variable to represent a whole block of stuff.
Then you should be using a class, with optional properties.
class Item(object):
def __init__(self, user, ...):
self.user = user
...
someitem = Item(me, ...)
print someitem.timeout
You can use "update" method to modify the contents of dictionary. Refer below example:
dict1 = {'a':1, 'b':2}
dict1.update({'c':3})
So create a original dictionary and use modify method to update it instead of storing the values in some temporary variable.
Despite your code in the question works or not, any Python assignment means assigning the reference to the object. This way, any assigned object is actually shared via one more reference. In other words, Python assignment means sharing.
If the shared chunk is constant, then you can look at it as optimization of the memory space usage. If the shared chunk is not constant, well, then it depends on the original intention.
Any Python variable is a reference to a target object.
Now, if I understand you well, you want to append new record to the history list. The 'timeout' is special, the ShortHis is to be shared. If this is the case, you have to use the new key for the shared chunk (say 'ref') and use the ShortHis chunk as the value for the key. Something like this:
record = { 'timeout': item.checkintime - item.checkouttime,
'ref': ShortHis }
item.history.append(record)
Like so perhaps?
class Item(object):
def __init__(self, user='', email='', phone=''):
self.user = user
self.email = email
self.phone = phone
self.time = ''
self.history = []
def add_short(self):
self.time = time.asctime()
self.history.append( {'user':self.user, 'time': self.time} )
def add_long(self):
self.time = time.asctime()
self.history.append( {'user':self.user, 'email': self.email,
'phone': self.phone, 'time': self.time } )
Example useage:
import time
from pprint import pprint as pp
item = Item('dudely', 'dudely#doright.com', '111-222-3333')
item.add_short()
time.sleep(1)
item.add_short()
time.sleep(1)
item.add_long()
time.sleep(1)
item.add_short()
time.sleep(1)
item.add_long()
pp(item.history)
Example output:
[{'time': 'Tue Jul 17 04:26:05 2012', 'user': 'dudely'},
{'time': 'Tue Jul 17 04:26:06 2012', 'user': 'dudely'},
{'email': 'dudely#doright.com',
'phone': '111-222-3333',
'time': 'Tue Jul 17 04:26:07 2012',
'user': 'dudely'},
{'time': 'Tue Jul 17 04:26:08 2012', 'user': 'dudely'},
{'email': 'dudely#doright.com',
'phone': '111-222-3333',
'time': 'Tue Jul 17 04:26:09 2012',
'user': 'dudely'}]