Best way to use variables across modules? (Python3) - python

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

Related

How can I refactor my code to return a collection of dictionaries?

def read_data(service_client):
data = list_data(domain, realm) # This returns a data frame
building_data = []
building_names = {}
all_buildings = {}
for elem in data.iterrows():
building = elem[1]['building_name']
region_id = elem[1]['region_id']
bandwith = elem[1]['bandwith']
building_id = elem[1]['building_id']
return {
'Building': building,
'Region Id': region_id,
'Bandwith': bandwith,
'Building Id': building_id,
}
Basically I am able to return a single dictionary value upon a iteration here in this example. I have tried printing it as well and others.
I am trying to find a way to store multiple dictionary values on each iteration and return it, instead of just returning one.. Does anyone know any ways to achieve this?
You may replace your for-loop with the following to get all dictionaries in a list.
naming = {
'building_name': 'Building',
'region_id': 'Region Id',
'bandwith': 'Bandwith',
'building_id': 'Building Id',
}
return [
row[list(naming.values())].to_dict()
for idx, row in data.rename(naming, axis=1).iterrows()
]

Dictionary key name from variable

I am trying to create a nested dictionary, whereby the key to each nested dictionary is named from the value from a variable. My end result should look something like this:
data_dict = {
'jane': {'name': 'jane', 'email': 'jane#example.com'},
'jim': {'name': 'jim', 'email': 'jim#example.com'}
}
Here is what I am trying:
data_dict = {}
s = "jane"
data_dict[s][name] = 'jane'
To my surprise, this does not work. Is this possible?
You want something like:
data_dict = {}
s = "jane"
data_dict[s] = {}
data_dict[s]['name'] = s
That should work, though I would recommend instead of a nested dictionary that you use a dictionary of names to either namedtuples or instances of a class.
Try this:
data_dict = {}
s = ["jane", "jim"]
for name in s:
data_dict[name] = {}
data_dict[name]['name'] = name
data_dict[name]['email'] = name + '#example.com'
as #Milad in the comment mentioned, you first need to initialize s as empty dictionary first
data={}
data['Tom']={}
data['Tom']['name'] = 'Tom Marvolo Riddle'
data['Tom']['email'] = 'iamlordvoldermort.com'
For existing dictionaries you can do dict[key] = value although if there is no dict that would raise an error. I think this is the code you want to have:
data_dict = {}
s = "jane"
data_dict[s] = {"name": s, "email": f"{s}#example.com"}
print(data_dict)
I just realized when I got a notification about this question:
data_dict = defaultdict(dict)
data_dict["jane"]["name"] = "jane"
Would be a better answer I think.

Methods to indent when using multiple list comprehension

Trying not to use too many variables in code, I came up with the code below. It looks horrible. Any ideas on how to format it nicely? Do I need to use more variables?
I write code like this a lot, and it'd help to see what methods people usually resort to have readable code while making creating less variables
exceptions = []
# find all the distinct parent exceptions (sorted) and add to the list
# with their children list
for parent in collection.find(
{'tags': 'exception'}).sort('viewPriority').distinct('parentException'):
group_info = {'groupName': parent,
'children': [{'value': ex['value'],
'label': ex['label'],}
for ex in collection.find({'tags': 'exception',
'parentException': parent}
).sort('viewPriority')],
}
exceptions.append(group_info)
I would break your logic up into functions
def get_children(parent):
result = collection.find({'tags': 'exception', 'parentException': parent})
result = result.sort('viewPriority')
return [{'value': ex['value'], 'label': ex['label']} for ex in result]
def get_group_info(parent):
return {'groupName': parent, 'children': get_children(parent)}
result = collection.find({'tags': 'exception'})
result = result.sort('viewPriority').distinct('parentException')
exceptions = [get_group_info(parent) for parent in result]
As a bonus, you can easily unittest get_children and get_group_info
Definitely difficult to get this to look any good, here is my best attempt at keeping the line lengths short and maintaining readability:
exceptions = []
# find all the distinct parent exceptions (sorted) and add to the list
# with their children list
for parent in (collection.find({'tags': 'exception'})
.sort('viewPriority').distinct('parentException')):
group_info = {
'groupName': parent,
'children': [{'value': ex['value'], 'label': ex['label'],}
for ex in (collection.find({'tags': 'exception',
'parentException': parent})
.sort('viewPriority'))],
}
exceptions.append(group_info)

Update and create a multi-dimensional dictionary in Python

I am parsing JSON that stores various code snippets and I am first building a dictionary of languages used by these snippets:
snippets = {'python': {}, 'text': {}, 'php': {}, 'js': {}}
Then when looping through the JSON I'm wanting add the information about the snippet into its own dictionary to the dictionary listed above. For example, if I had a JS snippet - the end result would be:
snippets = {'js':
{"title":"Script 1","code":"code here", "id":"123456"}
{"title":"Script 2","code":"code here", "id":"123457"}
}
Not to muddy the waters - but in PHP working on a multi-dimensional array I would just do the following (I am lookng for something similiar):
snippets['js'][] = array here
I know I saw one or two people talking about how to create a multidimensional dictionary - but can't seem to track down adding a dictionary to a dictionary within python. Thanks for the help.
This is called autovivification:
You can do it with defaultdict
def tree():
return collections.defaultdict(tree)
d = tree()
d['js']['title'] = 'Script1'
If the idea is to have lists, you can do:
d = collections.defaultdict(list)
d['js'].append({'foo': 'bar'})
d['js'].append({'other': 'thing'})
The idea for defaultdict it to create automatically the element when the key is accessed. BTW, for this simple case, you can simply do:
d = {}
d['js'] = [{'foo': 'bar'}, {'other': 'thing'}]
From
snippets = {'js':
{"title":"Script 1","code":"code here", "id":"123456"}
{"title":"Script 2","code":"code here", "id":"123457"}
}
It looks to me like you want to have a list of dictionaries. Here is some python code that should hopefully result in what you want
snippets = {'python': [], 'text': [], 'php': [], 'js': []}
snippets['js'].append({"title":"Script 1","code":"code here", "id":"123456"})
snippets['js'].append({"title":"Script 1","code":"code here", "id":"123457"})
print(snippets['js']) #[{'code': 'code here', 'id': '123456', 'title': 'Script 1'}, {'code': 'code here', 'id': '123457', 'title': 'Script 1'}]
Does that make it clear?

Assigning a Variable to represent a Block of Code

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'}]

Categories

Resources