So I want to create a function that accepts values for dictionary, creates new dictionary and returns it. Then I want to assign that value in other places of code. But I get Type Error dict object is not callable. How can I do this?
def shoe(brand, model):
shoe = {}
shoe['brand'] = brand
shoe['model'] = model
return shoe
shoes_list = []
shoe = {}
shoe = shoe('Some Brand', 'Some model')
print(shoe)
def shoe(brand, model):
Here you create a function named shoe.
shoe = {}
Now you create a variable named shoe and assign a dictionary to it. This isn't a problem yet since this variable is local inside of the function. However, it can be very confusing to reuse the same name in this way. I suggest changing this variable's name.
shoe['brand'] = brand
shoe['model'] = model
return shoe
shoes_list = []
shoe = {}
Now you are reassigning the name shoe to refer to an empty dictionary instead of the function that it used to refer to. This will cause later calls to the function with shoe() to fail.
shoe = shoe('Some Brand', 'Some model')
And then you try to assign the return value of the function to the name shoe(). I assume this is where the function call fails. Note that if you fix this, you don't need shoe = {} at all.
print(shoe)
You are using the same name for two different things: once for the name of your function and once for the name of your dictionary. Use two different names to fix the problem. In general, function names should be verbs. So you can do get_shoe() or create_shoe() for the function name. Then using the noun shoe for the dictionary will be fine.
You need your function and your original dict to have different names:
def make_shoe(brand, model):
shoe = {}
shoe['brand'] = brand
shoe['model'] = model
return shoe
shoes_list = []
shoe = {}
shoe = make_shoe('Some Brand', 'Some model')
print(shoe)
Note also that you are not filling in the dict you stored in shoe; you are making a new dict to replace it. If you want to fill it in, you would do something like this:
def fill_in(brand, model, dest):
dest['brand'] = brand
dest['model'] = model
shoes_list = []
shoe = {}
fill_in('Some Brand', 'Some model', shoe)
print(shoe)
Related
I'm trying to make a dictionary which calls the name of a restaurant, and the type of cuisine it is known for.
I wanted to create a dictionary so I could call each restaurant type up later. The problem is every method I've tried so far overwrites my value pairs which pertain to each key.
I understand that I somehow need to alter each key so as not to overwrite my values, but so far every attempt I've tried has not been successful. To circumvent that, I tried to place a dictionary within a dictionary. The input/output of the code is also seen below.
Some of the things I attempted are below:
restaurant_dict[restaurant] = value
main:
from nine_two import Restaurant
def increment(min_num, max_num, name_of_variable):
#enter the name of the variable and how many times to increment it
list_1 = []
for x in range(min_num,max_num):
list_1.append(f"{name_of_variable}_{x}")
for variable in list_1:
#return a list of the incremented variables
return list_1
#Created dictionaries & variables
restaurant_dict = {}
restaurant_dict_2 = {}
list_1 = increment(1,4,"Restaurant")
for variable in list_1:
print(f"\n{variable}:")
user_restaurant = input("Enter name of restaurant: ")
user_cuisine = input("Enter cuisine of restaurant: ")
#FIXME attempt to store variables in a callable dictionary
restaurant_dict_2[variable] = restaurant_dict
restaurant_dict["Restaurant"] = user_restaurant
restaurant_dict["Cuisine type"] = user_cuisine
print(restaurant_dict_2)
#ignore this
variable = Restaurant(user_restaurant,user_cuisine)
variable.describe_restaurant()
variable.open_restaurant()
Imported code is:
class Restaurant:
"""Creates a restaurant"""
def __init__(self, restaurant_name, cuisine_type):
"""Intialize name and cuisine attributes."""
self.name = restaurant_name
self.cuisine = cuisine_type
def describe_restaurant(self):
"""describes the restaurant"""
print(f"The {self.name} is a restaurant that specializes in {self.cuisine}.")
def open_restaurant(self):
"""Opens the restaurant"""
print(f"The {self.name} is now open!")
Output of code above
This answer comes in two parts:
How to fix your dictionary problem.
You don't need to use a dictionary at all.
Part 1: How to fix the problem with your dictionaries:
Your problem is that you assign the same restaurant_dict to multiple keys. When you do dict2['key1'] = dict1, you didn't create a copy of dict1 to assign to dict2['key1']. So when later you do dict2['key2'] = dict1, the same dict gets assigned to both key1 and key2. Then, when you change dict1['name'], since the same dict1 is referenced by both key1 and key2, it changes the value in both places.
To fix this, move the line restaurant_dict = {} inside your for variable in list_1: loop, so that you create a new dictionary for each restaurant.
#FIXME attempt to store variables in a callable dictionary
restaurant_dict = {}
restaurant_dict_2[variable] = restaurant_dict
restaurant_dict["Restaurant"] = user_restaurant
restaurant_dict["Cuisine type"] = user_cuisine
Or, better yet, you can simply assign the Restaurant and Cuisine type keys while you're creating the dict
#FIXME attempt to store variables in a callable dictionary
restaurant_dict = {"Restaurant": user_restaurant, "Cuisine type": user_cuisine}
restaurant_dict_2[variable] = restaurant_dict
Part 2: You don't need to use a dictionary at all
Usually when you use a dictionary, you want to map a key to a value. For example, if you were creating a directory of restaurants for every cuisine, you could have a dictionary where the keys were the cuisine type, and the values were a list of restaurants serving that cuisine.
However, in this case, it seems like you are simply making a list of restaurants (because your keys are sequential), so a simple list would suffice. Also, since you have a class to represent a restaurant, you just need to create an object of this class and append it to your list. No need to create dictionaries and Restaurant objects.
restaurants = []
for _ in range(num_restaurants):
user_restaurant = input("Enter name of restaurant: ")
user_cuisine = input("Enter cuisine of restaurant: ")
restaurant = Restaurant(user_restaurant, user_cuisine)
restaurants.append(restaurant)
restaurant.describe_restaurant()
restaurant.open_restaurant()
If you wanted to do the thing I mentioned in the previous paragraph and create a map of cuisines with restaurants, you could then process this list into a dict:
cuisine_restaurants = {}
for rest in restaurants:
cuisine = rest.cuisine
if cuisine not in cuisine_restaurants:
cuisine_restaurants[cuisine] = []
cuisine_restaurants[cuisine].append(rest)
Then, you could ask the user for a cuisine, and show all restaurants that serve that cuisine without having to re-iterate over the entire restaurants list:
c = input("What would you like to eat today?")
suggested_rest = cuisine_restaurants.get(c, []) # .get() returns the second argument if the key given by c doesn't exist
print("You could eat at: ")
for r in suggested_rest:
r.describe_restaurant()
I'm writing a script to find the moving average of different stocks. This script runs continuously, looping through my API call to add the current price to a list before averaging it. This works fine, however I'd like to be able to put this into a function to where the only input I need to give it is the name of the stock. I'd like this script to work for as many stocks as I want to specify, at the same time. That's where I run into issues because for each stock I have I need to have an empty list predefined that can hold the pricing information.
I've been trying to use the name of the stock to then create a related list, but as I now understand it it's not a great idea using one variable name to create another variable, so I'm not sure what to do. I believe the usual solution here would be to use a dictionary, but I'm a beginner to programming in general so I haven't figured out how to fit that into my situation. Any help would be greatly appreciated.
def sma(stock_name):
list_exists = stock_name + "_list" in locals() or stock_name + "_list" in globals()
if list_exists:
print()
else:
stock_name + "_list" = [] # Problem line, I would like for this to create a list called stock_name_list
stock_price = requests.get("myapi.com", params={"stock_name": stock_name, "bla bla": "blah"})
stock_name_list.append(stock_price)
When you have an operation based on a version of the data specific to that operation, that is usually a good time to think about using classes. This particular proposed class will encapsulate the name of a stock, the list of data specific to it, and perform sma on it:
class Stock:
n = 10
def __init__(self, name):
self.name = name
self.data = []
def sma(self):
stock_price = requests.get("myapi.com", params={"stock_name": self.stock_name, "bla bla": "blah"})
self.data.append(stock_price)
window = self.data[-n:]
return sum(window) / len(window)
Now you can maintain a dictionary of these objects. Any time you encounter a new stock, you just add an item to the dictionary:
stocks = {}
def sma(name):
stock = stocks.get(name)
if name is None: # None is what get returns when the key is missing
stock = Stock(name)
stocks[name] = stock
return stock.sma()
The nice thing is that you now have a dictionary of named datasets. If you want to add a different statistic, just add a method to the Stock class that implements it.
I defined a global sma function here that calls the eponymous method on the object it finds in your dictionary. You can carry encapsulation to an exterme by making the method perform the action of the function if called statically with a name instead of an instance. For example:
class Stock:
n = 10
named_stocks = {} # This is a class variable that replaces the global stocks
def __init__(self, name):
self.name = name
self.data = []
def sma(self):
if isinstance(self, str):
self = Stock.get_stock(self)
stock_price = requests.get("myapi.com", params={"stock_name": self.stock_name, "bla bla": "blah"})
self.data.append(stock_price)
window = self.data[-n:]
return sum(window) / len(window)
#classmethod
def get_stock(cls, name):
stock = cls.named_stocks.get(name)
if stock is None:
stock = cls(name)
cls.named_stocks[name] = stock
return stock
Now that there is a check for isinstance(self, str), you can call the sma method in one of two ways. You can all it directly on an instance, which knows its own name:
aapl = Stock('AAPL')
aapl.sma()
OR
Stock.get_stock('AAPL').sma()
Alternatively, you can call it on the class, and pass in a name:
Stock.sma('AAPL')
use defaultdict
from collections import defaultdict
stock_name_to_stock_prices = defaultdict(list)
stock_name_to_stock_prices['STOCK_NAME'].append(123.45)
I am currently working on an iTunes data program that's cycling constantly through a user's library to get statistics about one's library.
returns
I have a few code snippets like these:
def numArtist(self):
num = 0
for song in self.allSongs:
tempList = []
if song.artist not in tempList:
tempList.append(song.artist)
num += 1
return num
def getAlbumNames(self):
albums = []
for song in self.allSongs:
if song.album not in albums:
albums.append(song.album)
return albums
Where the main for loop body is repeated:
for song in self.allSongs: # same for-loop condition
# different for-loop body
Is there a way to refactor methods like these, where I have the same for loop conditions but with different body definitions?
I have a quite a few methods with the same for-loop, so I'd like to find a way to decrease the complexity and redundancy of my code.
Just for reference, all Song objects have attributes - artist, album (name),genre, etc - that I'm using to get my data.
Use set comprehensions and len to simplify each of them:
def numArtist(self):
return len({song.artist for song in self.allSongs})
def getAlbumNames(self):
return {song.album for song in self.allSongs}
To make it more generic, you could write a method that takes a lambda and use that to filter the property out of each song:
def uniqueProps(self, fxn):
return {fxn(song) for song in self.allSongs}
def getAlbumNames(self):
return self.uniqueProps(lambda song: song.album)
You can use set comprehensions for both snippets, if that counts as a valid "For-Loop refactoring":
artist_count = len({song.artist for song in self.allSongs})
album_names = set({song.album for song in self.allSongs})
Generic version using getattr
get_values = lambda objs, attr: {getattr(obj, attr) for obj in objs
attributes = 'artist', 'album'
values = [get_values(self.allSongs, name) for name in attributes]
artists, albums = values
artist_count = len(artists)
Generic version using lambda
get_artist = lambda song: song.artist
get_album = lambda song: song.album
getters = get_artist, get_album
values = [
{func(song) for song in self.allSongs}
for getter in getters
]
artists, albums = values
artist_count = len(artists)
Generic version using property
# If `song` is an instance of the `Song` class and both `artist` and
# `album` are properties defined on the class, it's also possible to
# directly use the property getter (`property.fget`) to avoid defining
# the lambdas manually:
get_artist = Song.artist.fget
get_album = Song.album.fget
... # <same as above>
If the contents of your allSongs list are immutable - which I suspect they are - you can convert your lists to sets and back to lists again - or use set comprehension - to get rid of duplicates. Then your functions can be greatly simplified like so:
def numArtist(self):
return len({song.artist for sing in self.allSongs})
def getAlbumNames(self):
return list({song.album for song in self.allSongs})
If you're not sure if the song objects are mutable or not, try this out anyway. If they're mutable objects you'll get an exception like:
TypeError: unhashable type: ...
You could try to create the generators, that produces the value of song attributes. Let me give you an example:
def gen_attr(songs, attr_name):
for song in songs:
yield getattr(song, attr_name)
class Song(object):
def __init__(self, name, artist):
self.name = name
self.artist = artist
class Album(object):
def __init__(self, songs_list):
self.songs_list = songs_list
def allSongs(self):
return self.songs_list
s = Song('Ahoy', 'Pirate')
s1 = Song('Bye', 'My Son')
s2 = Song('Ahoy', 'Captain')
a = Album([s, s1])
Now if you want to get all of the song names, u can use:
song_names = list(gen_attr(a.allSongs(), 'name'))
print(song_names) # ['Ahoy', 'Bye', 'Ahoy']
For non-repeated song names you would use:
song_names = list(set(gen_attr(a.allSongs(), 'name')))
print(song_names) # ['Ahoy', 'Bye']
To count the non-repeated artists names, you would use:
artists = len(set(gen_attr(a.allSongs(), 'artist')))
And to create the list of artists, just go for:
artists = list(gen_attr(a.allSongs(), 'artist'))
print(artists) # ['Pirate', 'My Son', 'Captain']
I need to create a class that that stores tax data as default values, but that can be populated with actual data from tax returns. So for instance, let's say I have three categories: income, statutory adjustments, and tax computation, each which contains several attributes. Ideally, I would like to have three self arguments that I can pass around to parts of a tax calculator. But I would want to be able to pass around each attribute in the self string individually. Right now I have this:
class taxReturn:
def __init__(self):
self.income = [attribute1, attribute2, attribute3]
self.statut = [attribute4, attribute5, attribute6]
self.tax_comp = [attribute7, attribute8, attribute9]
Each attribute would have to be set to a default value that could be passed around, but then later filled in with actual data.
Any help would be appreciated. Thanks!
Why not just define this in the __init__ method?
class taxReturn:
def __init__(self, income = [attribute1, attribute2, attribute3],statut = [attribute4, attribute5, attribute6],tax_comp = [attribute7, attribute8, attribute9]):
self.income = income
self.statut = statut
self.tax_comp = tax_comp
# To create instance of taxReturn:
txR = taxReturn() # income, statut and tax_comp will take the default values defined bellow
//OR
txR = taxReturn(income = NewListOfAttribute1, statut=NewListOfAttribute2,tax_comp=NewListOfAttribute3) # income, statut and tax_comp will take the new attribute defined
So I have a model Card. In project I create the cardData wrapper of this model:
card = Card.objects.get(id=someId)
cardData = {}
cardData.id = card.id
cardData.title = card.title
Than I put over 30 cardDatas in deck list:
deck = [cardData1, cardData2 ..., cardData17]
Finally somewhere in game I have a link for example on cardCata17. Which contains in deck list. But at the same time there is also cardData2 whith the same params becouse it is also was created from the same model. And when I try to get index of cardData17:
index = deck.index(cardData17)
It return index = 2, and not 17 because the cardData two also has same id and title. That is rather strange and I spend a lot of time to understand this problem. Is it real or I mistake in this assumption.
Make objects of card data instead of dictionaries. The objects will be identified by their memory location, which is unique, instead of by their content.
class CardData(object):
def __init__(self, id, title):
self.id = id
self.title = title
card = Card.objects.get(id=someId)
cardData = CardData(card.id, card.title)