How to provide different values for the same object? - python-3.x

I am new to Python and trying to make a simple fishing game. I would like to have the object 'salmon' to be added to inventory more than once with different values for 'weight' and 'resist'. With the inventory.add_item function below, it appears that either only one item is being added to the inventory or the same item is added twice with the same values.
How am I able to get the same type of object with different values?
import random
class Inventory(object):
def __init__(self):
self.fishes = {}
def add_item(self, fish):
self.fishes[fish.species] = fish
def print_items(self):
print('\t'.join(['Weight', 'Resist', 'Species']))
for fish in self.fishes.values():
print('\t'.join([str(x) for x in [fish.weight, fish.resist, fish.species]]))
inventory = Inventory()
class Fish(object):
def __init__(self, weight, resist, species):
self.weight = weight
self.resist = resist
self.species = species
salmon = Fish(random.randint(2, 10), random.randint(5, 7), 'Atlantic Salmon')
print('Going fishing...\nCaught a Salmon!')
inventory.add_item(Fish(salmon.weight, salmon.resist, salmon.species))
inventory.add_item(Fish(salmon.weight, salmon.resist, salmon.species))
inventory.print_items()

Try this:
salmon = Fish(random.randint(2, 10), random.randint(5, 7), 'Atlantic Salmon')
salmon2 = Fish(random.randint(2, 10), random.randint(5, 7), 'Atlantic Salmon2')
print('Going fishing...\nCaught a Salmon!')
inventory.add_item(salmon)
inventory.add_item(salmon2)
inventory.print_items()
First the key for the dictionary must be unique:
fish.species
also the object you add, because of the key...

Related

How to get the the vector of mouse position in python ursina

I'm making a Platform-Maker and I need to get the position of the mouse, then I place a object in there. Then I need to store it into a array so I can convert it to code later on. Right now I have to store it into a array, but when I do that it Gives me an empty array:
Edit On Image: This array is suppose to be containing A Vector3 Position where the mouse was clicked.
from ursina import *
from ursina.prefabs.sky import Sky
from ursina.shaders import basic_lighting_shader
app = Ursina(borderless = False)
window.fullscreen = True
window.fps_counter.enabled = False
editor = EditorCamera()
camera.fov = 90
class StartPlatform(Entity):
def __init__(self):
super().__init__(
parent = scene,
model = "cube",
position = (0, 0, 0),
scale = (10, 2, 10),
collider = "mesh",
shader = basic_lighting_shader,
texture = "white_cube",
color = color.lime
)
class AddPlatform(Entity):
def __init__(self, position = (0, 0, 0)):
super().__init__(
parent = scene,
model = "cube",
position = position,
scale = (10, 2, 10),
collider = "mesh",
shader = basic_lighting_shader,
texture = "white_cube",
color = color.azure
)
def input(key):
if held_keys["escape"]:
quit()
def update():
if held_keys["left mouse"]:
platform= AddPlatform(position = (mouse.world_point, mouse.world_point, mouse.world_point))
platforms = []
for i in platforms:
platform[i].append(platforms)
print(platforms)
Sky()
StartPlatform()
app.run()
Firt off: py position = (mouse.world_point, mouse.world_point, mouse.world_point)
is wrong, it should be
position = mouse.world_point
Secondly, this part doesn't make sense.
platforms = []
for i in platforms:
platform[i].append(platforms)
print(platforms)
Here's what it does:
platforms = [] Every frame, create a new list called platforms
for i in platforms: loop through every element. However you just set it to be an empty list, so this will just be skipped since there's nothing there.
platform[i].append(platforms) platform is a Entity you instantiated earlier. What is [i] of that? You can't get a member of a an entity that way.
print(platforms) This would return [] since it's not been changed.
If what you want to do is to add the platform you created to a list, you'd define the list outside of the function and add the append the platform to it. You're also using update for this, which gets called every frame. This means you'll probably be creating about 60 platforms every second you're holding the mouse button down. You probably want to use input instead.
platforms = []
def input(key):
if key == 'left mouse down' and mouse.world_point:
platform = AddPlatform(position=mouse.world_point)
platforms.append(platform)

Can I store a function rather than its result to a dictionary value so that it is dynamically updated when the value is called?

I have a dictionary:
mydict = {'x':val1,'x2':val2}
I would like to do something like
mydict['x3'] = some_func(mydict['x1'])
Where if I later change mydict['x1'] and call mydict['x3'], I return the output of some_func() with the new value of x1.
I realize this is something class property decorators handle well. I'd like to also be able to change the function stored at the 'x3' key for that specific object. This is something class properties cannot do to my knowledge (having the underlying function definition changed on an instance of the class).
You can just set 'x3' to a function in your dict. Then whenever you want to use it, you can call it. Something like this:
mydict = {'x1': val1,'x2': val2, 'x3': lambda: some_func(mydict['x1'])}
Now, mydict['x3'] is a function.
x3 = mydict['x3']()
print(x3)
Here's a quick demo
At this point, you should define a class rather than using a dict directly.
class MyClass:
def __init__(self, x1, x2):
self.x1 = x1
self.x2 = x2
#property
def x3(self):
return some_func(self.x1)
mydict = {'x':val1,'x2':val2}
m = MyClass(mydict['x'], mydict['x2'])
assert m.x3 == some_func(m.x1)
Based on primarily on Rocket's answer, I came up with this test solution.
class Test:
def __init__(self, x, y):
self.x = x
self.y = y
def set_prop(self, name, func):
setattr(Test, name, property(fget=func))
test = Test(1,2)
### Print list of class attributes in __dict__
for key, val in test.__dict__.items():
print(f"{key} = {val}")
### Create new property, xy defined by self.x * self.y
test.set_prop(name='xy', func=lambda self: self.x*self.y)
print(f"test.x ({test.x}) * test.y ({test.y}) = {test.xy}")
### Test if it is properly linked to changes in x
test.x = 2
print(f"test.x set to {test.x}")
print(f"test.x ({test.x}) * test.y ({test.y}) = {test.xy}")
### Test changing the underlying property formula
test.set_prop('xy',lambda self: self.x/self.y)
print(f"test.x ({test.x}) / test.y ({test.y}) = {test.xy}")

Write class such that calling instance returns all instance variables

I have answered my own question - see answer below
I'm writing a class, and I want this behavior:
a = f(10,20)
some_funct(a.row) # some_function is given 10
some_funct(a.col) # some_function is given 20
some_funct(a) # some_function is given a tuple of 10, 20 <-- THIS ONE :)
The last behavior is stumping me. I have not seen any examples that cover this.
Thus far:
class f(object):
"""Simple 2d object"""
row: int
col: int
def __init__(self, row, col):
self.row = row
self.col = col
Explictly I do not want another method, say, self.both = row, col.
I just want to 'call' the instance
I'm new to classes, so any improvements are welcome. Properties, setters, getters etc.
EDIT 1:
Replaced "print" with "some_function" in the question, and modified title
You can do like this
class f(object):
"""Simple 2d object"""
row: int
col: int
def __init__(self, row, col):
self.row = row
self.col = col
def __str__(self):
return f"row = {row}, col = {col}"
and print like this
a = f(10,20)
print(a) # row = 10, col = 20
This might help
class f(object):
"""Simple 2d object"""
row: int
col: int
def __init__(self, row, col):
self.row = row
self.col = col
def some_funct(self):
return (self.row, self.col)
You can access like
a = f(10,20)
a.some_funct() # (10, 20)
# or
row, col = a.some_funct()
From python 3.7 dataclasses have been introduced and their goal is to create classes that mainly contains data. Dataclasses comes with some helper function that extract the class attributes dict/tuples. e.g.
from dataclasses import dataclass,asdict,astuple
#dataclass
class f:
x: int
y: int
f_instance = f(10,20)
asdict(f_instance) # --> {'x': 10, 'y': 20}
astuple(f_instance) # -> (10,20)
EDIT I : Another technique would be to use namedtuple e.g.:
from collections import namedtuple
f = namedtuple('p',['row','col'])
a =f(10,20)
a.row #-> 10
a.col #-> 20
class f(tuple):
"""Simple 2d object"""
def __new__(cls, x, y):
return tuple.__new__(f, (x, y))
def __init__(self, x, y):
self.col = x
self.row = y
foo = f(1,2)
print(foo.col)
>>>1
print(foo.row)
>>>2
print(foo)
>>>(1, 2)
Importantly:
If you want it to behave like a tuple then make it a subclass of tuple.
Much stuffing around but stumbled upon an external site which gave me clues about the keywords to search on here. The SO question is here but I have modified that answer slightly.
I'm still a little confused because the other site says to use new in the init as well but does not give a clear example.

How to create a dataframe of a particular size containing both continuous and categorical values with a uniform random distribution

So, I'm trying to generate some fake random data of a given dimension size. Essentially, I want a dataframe in which the data has a uniform random distribution. The data consist of both continuous and categorical values. I've written the following code, but it doesn't work the way I want it to be.
import random
import pandas as pd
import time
from datetime import datetime
# declare global variables
adv_name = ['soft toys', 'kitchenware', 'electronics',
'mobile phones', 'laptops']
adv_loc = ['location_1', 'location_2', 'location_3',
'location_4', 'location_5']
adv_prod = ['baby product', 'kitchenware', 'electronics',
'mobile phones', 'laptops']
adv_size = [1, 2, 3, 4, 10]
adv_layout = ['static', 'dynamic'] # advertisment layout type on website
# adv_date, start_time, end_time = []
num = 10 # the given dimension
# define function to generate random advert locations
def rand_shuf_loc(str_lst, num):
lst = adv_loc
# using list comprehension
rand_shuf_str = [item for item in lst for i in range(num)]
return(rand_shuf_str)
# define function to generate random advert names
def rand_shuf_prod(loc_list, num):
rand_shuf_str = [item for item in loc_list for i in range(num)]
random.shuffle(rand_shuf_str)
return(rand_shuf_str)
# define function to generate random impression and click data
def rand_clic_impr(num):
rand_impr_lst = []
click_lst = []
for i in range(num):
rand_impr_lst.append(random.randint(0, 100))
click_lst.append(random.randint(0, 100))
return {'rand_impr_lst': rand_impr_lst, 'rand_click_lst': click_lst}
# define function to generate random product price and discount
def rand_prod_price_discount(num):
prod_price_lst = [] # advertised product price
prod_discnt_lst = [] # advertised product discount
for i in range(num):
prod_price_lst.append(random.randint(10, 100))
prod_discnt_lst.append(random.randint(10, 100))
return {'prod_price_lst': prod_price_lst, 'prod_discnt_lst': prod_discnt_lst}
def rand_prod_click_timestamp(stime, etime, num):
prod_clik_tmstmp = []
frmt = '%d-%m-%Y %H:%M:%S'
for i in range(num):
rtime = int(random.random()*86400)
hours = int(rtime/3600)
minutes = int((rtime - hours*3600)/60)
seconds = rtime - hours*3600 - minutes*60
time_string = '%02d:%02d:%02d' % (hours, minutes, seconds)
prod_clik_tmstmp.append(time_string)
time_stmp = [item for item in prod_clik_tmstmp for i in range(num)]
return {'prod_clik_tmstmp_lst':time_stmp}
def main():
print('generating data...')
# print('generating random geographic coordinates...')
# get the impressions and click data
impression = rand_clic_impr(num)
clicks = rand_clic_impr(num)
product_price = rand_prod_price_discount(num)
product_discount = rand_prod_price_discount(num)
prod_clik_tmstmp = rand_prod_click_timestamp("20-01-2018 13:30:00",
"23-01-2018 04:50:34",num)
lst_dict = {"ad_loc": rand_shuf_loc(adv_loc, num),
"prod": rand_shuf_prod(adv_prod, num),
"imprsn": impression['rand_impr_lst'],
"cliks": clicks['rand_click_lst'],
"prod_price": product_price['prod_price_lst'],
"prod_discnt": product_discount['prod_discnt_lst'],
"prod_clik_stmp": prod_clik_tmstmp['prod_clik_tmstmp_lst']}
fake_data = pd.DataFrame.from_dict(lst_dict, orient="index")
res = fake_data.apply(lambda x: x.fillna(0)
if x.dtype.kind in 'biufc'
# where 'biufc' means boolean, integer,
# unicode, float & complex data types
else x.fillna(random.randint(0, 100)
)
)
print(res.transpose())
res.to_csv("fake_data.csv", sep=",")
# invoke the main function
if __name__ == "__main__":
main()
Problem 1
when I execute the above code snippet, it prints fine but when written to csv format, its horizontally positioned; i.e., it looks like this... How do I position it vertically when writing to csv file? What I want is 7 columns (see lst_dict variable above) with n number of rows?
Problem 2
I dont understand why the random date is generated for the first 50 columns and remaining columns are filled with numerical values?
To answer your first question, replace
print(res.transpose())
with
res.transpose() print(res)
To answer your second question look at the length of the output of the method
rand_shuf_loc()
it as well as the other helper functions only produce a list of 50 items.
The creation of res using the method
fake_data.apply
replaces all nan with a random numeric, so it also applies a numeric to the columns without any predefined values.

Set model field default value set based on previous object's value from model class

I set model managers to query the request_number of the last record in the database. The default value of the request number should be 210001 (if the first record of the year 2021), or the next sequential number of the current year (20002, 20003, 20004, etc).
How do I set the code to look at the previous record as part of setting the default value of the model field?
from django.db import models
import datetime
class ServiceRequestManager(models.Manager):
def last_record(self):
last_record_year = int(self.last().request_number[0:2])
return last_record_year
def last_record_request_number_plus_one(self):
last_request_number = int(self.last().request_number) + 1
return last_request_number
def test(self):
year_difference = int(datetime.date.today().strftime('%y')) - int(self.last().request_number[0:2])
return year_difference
def number():
year = str(datetime.date.today().strftime('%y')) # Pulls last two digits of the year
# previous_record_year = ServiceRequestManager.last_record
# new_request_number = ServiceRequestManager.last_record_request_number_plus_one
# current_record_year = int(datetime.date.today().strftime('%y'))
if int(year) > ServiceRequestManager.last_record(): # TypeError: last_record() missing 1 required positional argument: 'self'
return year
else:
i = 1
i_pad = "%04d" % i # creates 000x so default is 0001
return year+str(i_pad) # Creates the full request number e.g. 200001
# Need the next record to be 200002, 200003, 200004 etc until the first instance of 2021 which would be 210001, 210002, etc.
# Create your models here.
class ServiceRequest(models.Model):
CATEGORY_CHOICES = (
(None, ''),
('aircraft_repair', 'Aircraft Repair'),
('backshop', 'Backshop'),
('documentation', 'Documentation'),
('other', 'Other')
)
PRIORITY_CHOICES = (
(None, ''),
('1', '1-Critical (<24 hours)'),
('2', '2-Urgent (1-2 Days)'),
('3', '3-Standard (3 Days)'),
('4', '4-Low (5 Days)')
)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False, blank=True)
updated = models.DateTimeField(auto_now=True)
request_number = models.CharField(max_length=6, default=number())
objects = ServiceRequestManager()
def __str__(self):
return self.request_number # show the request number in admin screen
class Meta:
ordering = ('-request_number',) # sort request number descending in admin screen
We can make a function that looks for the ServiceRequest that starts with the first two digits of the year, and then look for the last one of these records.
If no such record exists, we return f'{y2}0000' (so we "start" a new sequence), and if it does, we parse it to an int, then increment it, and obtain the last four digits to create a new one:
from django.utils.timezone import now
def number():
y2 = now().strftime('%y')
last_request = ServiceRequest.objects.filter(
request_number__startswith=y2
).order_by('-request_number').values_list('request_number', flat=True).first()
if last_request is None:
return f'{y2}0000'
else:
num = (int(last_request) + 1) % 10000
return f'{y2}{num:04d}'
In the model, we pass default=number, so a reference to the function, not the result of calling the function. Furthermore it might be useful to specify a database index, and make the field unique (to prevent generating two records with the same reference_number in the database):
class ServiceRequest(models.Model):
CATEGORY_CHOICES = (
(None, ''),
('aircraft_repair', 'Aircraft Repair'),
('backshop', 'Backshop'),
('documentation', 'Documentation'),
('other', 'Other')
)
PRIORITY_CHOICES = (
(None, ''),
('1', '1-Critical (<24 hours)'),
('2', '2-Urgent (1-2 Days)'),
('3', '3-Standard (3 Days)'),
('4', '4-Low (5 Days)')
)
timestamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
request_number = models.CharField(
max_length=6,
db_index=True,
default=number,
unique=True
)
Perhaps four digits is however not enough. It might be more safe to use max_length=8 for example, to allow 1'000'000 request numbers per year. Especially since some request numbers are perhaps not valid (and will thus be closed).

Resources