Understanding how Python Objects and Classes Work - python

I am trying to follow instructions from Effective Computation in Physics, a Field Guide to Research with Python book (by Anthony Scopatz and Kathryn Duff. In the OOP chapter(specifically page 136 in my version), I tried copying the code and ran the code below:
# import the Particle class from the particle module
from particle import Particle as p
# create an empty list to hold observed particle data
obs = []
# append the first particle
obs.append(p.Particle())
# assign its position
obs[0].r = {'x': 100.0, 'y': 38.0, 'z': -42.0}
# append the second particle
obs.append(p.Particle())
# assign the position of the second particle
obs[1].r = {'x': 0.01, 'y': 99.0, 'z': 32.0}
# print the positions of each particle
print(obs[0].r)
print(obs[1].r)
The result is supposed to give the position typed in. However, the code did not work like this. Instead, I played around with the code and this code worked instead:
# Import the Particle class from the particle Module
from particle import Particle as p
# Create an empty list
obs = []
# Append first element
obs.append(p)
# Assign its position
obs[0].r = {'x': 100.0, 'y': 38.0, 'z': -42.0}
# Append second particle
obs.append(particle())
# Assign second position
obs[1].r = {'x': 0.01, 'y': 99.0, 'z': 32.0}
print(obs[0].r)
print(obs[1].r)
I would love to understand why and what is going on. I am currently reviewing how OOP works and am using Python for now. Please respond! I want to learn how and why does this work!

I'm not sure I understood everything but if you want to turn your code in POO you can try something like this:
from particle import Particle as p
class Myclass:
def __init__(self):
# Create an empty list
self.obs = []
# Append first element
self.obs.append(p)
# Assign its position
self.obs[0].r = {'x': 100.0, 'y': 38.0, 'z': -42.0}
# Append second particle
self.obs.append(particle())
# Assign second position
self.obs[1].r = {'x': 0.01, 'y': 99.0, 'z': 32.0}
#if you want to call getobs from here use :
#self.getobs1()
def getobs0(self):
print(self.obs[0].r)
def getobs1(self):
print(self.obs[1].r)
if __name__ == '__main__':
myClass = Myclass() #create an object of Myclass and call __init__()
myClass.getobs0()
myClass.getobs1()

Related

Using brute force a dictionary of lists

I have a task that has to use brute force to solve.
The data is in a python dictionary where one value from each key is used to get correctly answer a sum is part of a wider solution so in that context there is only one solution but in the example, I am giving there is a possibility for multiple solutions I suppose. So let's just assume that the first solution is the correct one.
So for example say the target is 9036 The "rules" are that all we can do is addition and have to take one element from each list within the dictionary.
Therefore 9036 can be calculated from the dictionary below as x:9040,y:1247,w:242,z:-1493
I have been trying to achieve this via a loop but I cant get the code to "loop" how I want it to which is along the lines of iterating over x[0],y[0],w[0],z[0] where 0 is just the first element in the list in the first itteration and then doing x[0],y[1],w[0],z[0], x[0],y[1],w[1],z[0], x[0],y[1],w[1],z[1]... until it has solved the problem.
I have not added any code as I was simply running a loop that never got anywhere near what I needed it to do and as I have no real experience with these sorts of algorithms / brute-forcing I was hoping someone could help point me in the right direction.
EDIT::: I have added the dictionary so that a solution can be provided but the dictionary size can vary so it needs to be a dynamic solution.
The dictionary:
{'x': [11909.0, 9040.0], 'y': [4345.0, 1807.0, 1247.0, 0.0, 6152.0, 4222.0, 123.0], 'w': [538.0, 12.0, 526.0, 0.0, 242.0, 1.0, 128.0, 155.0], 'z': [7149.0, 3003.0, 4146.0, 3054.0, 0.0, -51.0, 1010.0, 189.0, 182.0, -1493.0, 5409.0, -1151.0]}
With inputs from https://stackoverflow.com/a/61335465/14066512 to iterate over dictionary.
permutations_dicts variable contains all the different types of "brute force" combinations
import itertools
keys, values = zip(*d.items())
permutations_dicts = [dict(zip(keys, v)) for v in itertools.product(*values)]
for i in permutations_dicts:
if sum(i.values())==9036:
print(i)
break
{'x': 9040.0, 'y': 1247.0, 'w': 242.0, 'z': -1493.0}
From what I understood you want to try all combinations of values for each key until you reach the right answer.
This will add all possible values for each key until it finds 9036:
my_dict = {'x': [11909.0, 9040.0], 'y': [4345.0, 1807.0, 1247.0, 0.0, 6152.0, 4222.0, 123.0], 'w': [538.0, 12.0, 526.0, 0.0, 242.0, 1.0, 128.0, 155.0], 'z': [7149.0, 3003.0, 4146.0, 3054.0, 0.0, -51.0, 1010.0, 189.0, 182.0, -1493.0, 5409.0, -1151.0]}
looping = True
for x in range(len(my_dict['x'])):
if not looping:
break
for y in range(len(my_dict['y'])):
if not looping:
break
for w in range(len(my_dict['w'])):
if not looping:
break
for z in range(len(my_dict['z'])):
addition = my_dict['x'][x] + my_dict['y'][y] + my_dict['w'][w] + my_dict['z'][z]
if addition == 9036:
new_dict = {'x':my_dict['x'][x], 'y':my_dict['y'][y], 'w':my_dict['w'][w], 'z':my_dict['z'][z]}
print(f"Correct answer is {new_dict}")
looping = False
break

Why do I get Type Error on one line, but not the others

So, I have this piece of code which is used to add/subtract from the x and y value in a list with dictionaries. The thing is, the code must account for the fact that the dictionary can be given like a string with quotes around it. So, for that reason I use ast.literal_eval(point) to turn the string into a dictionary. However, for some reason with this line that has been changed following commands don't work like they do with the others.
Commands in question: point['x'] += offset['x'] and point['y'] += offset['y']
I am very confused, both with the issue in question and the code in general as it was translated from js.
import ast
def parse(point):
if type(point) == dict:
return point
else:
return ast.literal_eval(point)
def shift(offset, points):
modified_points = points.copy()
for point in modified_points:
arg_type = type(point)
parse(point)
point['x'] += offset['x']
point['y'] += offset['y']
print(point)
return modified_points
polyline = [
{'x': 0,'y': 0},
{'x': 10, 'y': 10},
'{"x": 20, "y": 20}',
{'x': 30, 'y': 30}
]
results = shift({'x': 10, 'y': -5}, polyline)
print(results)
The error goes as following:
File "/home/selby/PycharmProjects/10thPractical/3rd.py", line 37, in <module>
results = shift({'x': 10, 'y': -5}, polyline)
File "/home/selby/PycharmProjects/10thPractical/3rd.py", line 19, in shift
point['x'] += offset['x']
TypeError: string indices must be integers
Process finished with exit code 1
You do not store the return value when you call parse(point), i.e. point is still a string when you try to use it in point['x'] += offset['x'] (as the error indicates).
Replace the line with point = parse(point), and the error will disappear.

How to iterate over a dictionary and operate with its elements?

I have this dictionary, where the keys represent atom types and the values represent the atomic masses:
mass = {'H': 1.007825, 'C': 12.01, 'O': 15.9994, 'N': 14.0067, 'S': 31.972071,
'P': 30.973762}
what I want to do is to create a function that given a molecule, for instance ('H2-N-C6-H4-C-O-2H'), iterates over the mass dictionary and calculates the atomic mass on the given molecule. The value of the mass must be multiplied by the number that comes right after the atom type: H2 = H.value * 2
I know that firstly I must isolate the keys of the given molecules, for this I could use string.split('-'). Then, I think I could use and if block to stablish a condition to accomplish if the key of the given molecule is in the dictionary. But later I'm lost about how I should proceed to find the mass for each key of the dictionary.
The expected result should be something like:
mass_counter('H2-N15-P3')
out[0] 39351.14
How could I do this?
EDIT:
This is what I've tried so far
# Atomic masses
mass = {'H': 1.007825, 'C': 12.01, 'O': 15.9994, 'N': 14.0067, 'S': 31.972071,
'P': 30.973762}
def calculate_atomic_mass(molecule):
"""
Calculate the atomic mass of a given molecule
"""
mass = 0.0
mol = molecule.split('-')
for key in mass:
if key in mol:
atom = key
return mass
print calculate_atomic_mass('H2-O')
print calculate_atomic_mass('H2-S-O4')
print calculate_atomic_mass('C2-H5-O-H')
print calculate_atomic_mass('H2-N-C6-H4-C-O-2H')
Given all components have the shape Aa123, It might be easier here to identify parts with a regex, for example:
import re
srch = re.compile(r'([A-Za-z]+)(\d*)')
mass = {'H': 1.007825, 'C': 12.01, 'O': 15.9994, 'N': 14.0067, 'S': 31.972071, 'P': 30.973762}
def calculate_atomic_mass(molecule):
return sum(mass[a[1]]*int(a[2] or '1') for a in srch.finditer(molecule))
Here our regular expression [wiki] thus captures a sequence of [A-Z-a-z]s, and a (possibly empty) sequence of digits (\d*), these are the first and second capture group respectively, and thus can be obtained for a match with a[1] and a[2].
this then yields:
>>> print(calculate_atomic_mass('H2-O'))
18.01505
>>> print(calculate_atomic_mass('H2-S-O4'))
97.985321
>>> print(calculate_atomic_mass('C2-H5-O-H'))
46.06635
>>> print(calculate_atomic_mass('H2-N-C6-H4-C-O-2H'))
121.130875
>>> print(calculate_atomic_mass('H2-N15-P3'))
305.037436
We thus take the sum of the mass[..] of the first capture group (the name of the atom) times the number at the end, and we use '1' in case no such number can be found.
Or we can first split the data, and then look for a atom part and a number part:
import re
srch = re.compile(r'^([A-Za-z]+)(\d*)$')
def calculate_atomic_mass(molecule):
"""
Calculate the atomic mass of a given molecule
"""
result = 0.0
mol = molecule.split('-')
if atm in mol:
c = srch.find(atm)
result += result[c[1]] * int(c[2] or '1')
return result
Here is an answer without regex:
import string
# Atomic masses
masses = {'H': 1.007825, 'C': 12.01, 'O': 15.9994, 'N': 14.0067, 'S': 31.972071,
'P': 30.973762}
def calculate_atomic_mass(molecule):
"""
Calculate the atomic mass of a given molecule
"""
mass = 0.0
for key in molecule.split('-'):
# check if any number is available
if not key[-1] in string.digits:
el, n = key, 1
# check length of element label (1 or 2)
elif key[1] in string.digits:
el, n = key[:1], int(key[1:])
else:
el, n = key[:2], int(key[2:])
mass += masses[el]*n
return mass
print calculate_atomic_mass('H2-O')
print calculate_atomic_mass('H2-S-O4')
print calculate_atomic_mass('C2-H5-O-H')
print calculate_atomic_mass('H2-N-C6-H4-C-O-H2')
Here's how I would do it. You don't really need to iterate over the dictionary. Instead you need to iterate over the atom(s) in the molecule and look things up (randomly) in the dictionary.
Here's an example of doing that which assumes that there'll never be more that 10 atoms of any kind making up the molecule and the each element's name is only one letter long.
# Atomic masses.
MASSES = {'H': 1.007825, 'C': 12.01, 'O': 15.9994, 'N': 14.0067, 'S': 31.972071,
'P': 30.973762}
def calculate_atomic_mass(molecule):
""" Calculate the atomic mass of a given molecule. """
mass = 0.0
for atom in molecule.split('-'):
if len(atom) == 1:
mass += MASSES[atom]
else:
atom, count = atom[0], atom[1]
mass += MASSES[atom] * int(count)
return mass
print calculate_atomic_mass('H2-O') # -> 18.01505
print calculate_atomic_mass('H2-S-O4') # -> 97.985321
print calculate_atomic_mass('C2-H5-O-H') # -> 46.06635
print calculate_atomic_mass('H2-N-C6-H4-C-O-H2') # -> 122.1387

Converting CNC drillings from old to new system (using Python)

I have this kind of file (part):
H DX=615 DY=425 DZ=22.15 -AB C=0 T=0 R=999 *MM /"def" BX=2.5 BY=452.5 BZ=25 ;M20150710.
XBO X=100 Y=50 Z=5 V=1000 R=0 x=0 y=0 D=10 N="P" F=1 ;Test F1/10P.
...
which I want to convert to a new programming system. What I want to do is first read the header (H) and put the DX, DY and DZ values in respectively named variables. I managed to do this, but when I came to process my XBO line (a drilling, from which I need X, Y, Z, V, R, x, y, D, N, F and ;, also in separate variables) my code started looking very ugly very fast.
So I started over, and came up with this:
f = open("input.xxl") # open input file
for line in f:
if Debug==1: print line
for char in line:
charbuffr=charbuffr+char
if "H" in charbuffr:
if Debug==1: print'HEADER found!'
charbuffr=""
if "XBO" in charbuffr:
if Debug==1: print'XBO found!'
charbuffr=""
This correctly identifies the separate commands H and XBO, but I'm kind of stuck now. I can use the same method to extract all the variables, from loops inside the H and XBO loops, but this does not seem like good coding...
Can anyone set me on the right foot please? I don't want a full solution, as I love coding (well my main job is coding for CNC machines, which seems easy now compared to Python), but would love to know which approach is best...
Instead of converting data types by hand, you could use ast. literal_eval. This helper function takes a list of the form ['a=2', 'b="abc"'] and converts into a dictionary {'a': 2, 'b': 'abc'}:
import ast
def dict_from_row(row):
"""Convert a list of strings in the form 'name=value' into a dict."""
res = []
for entry in row:
name, value = entry.split('=')
res.append('"{name}": {value}'.format(name=name, value=value))
dict_string = '{{{}}}'.format(', '.join(res))
return ast.literal_eval(dict_string)
Now parsing the file becomes a bit simpler:
for line in f:
row = line.split()
if not row:
continue
if row[0] == 'H':
header = dict_from_row(row[1:4])
elif line[0] == 'XBO':
xbo = dict_from_row(row[1:11])
Results:
>>> header
{'DX': 615, 'DY': 425, 'DZ': 22.15}
>>> xbo
{'D': 10, 'F': 1, 'R': 0, 'V': 1000, 'X': 100, 'Y': 50, 'Z': 5, 'x': 0, 'y': 0}
As an inspiration, you can do something like this:
for raw_line in f:
line = raw_line.split()
if not line:
continue
if line[0] == 'H':
header = {}
for entry in line[1:4]:
name, value = entry.split('=')
header[name] = float(value)
elif line[0] == 'XBO':
xbo = {}
for entry in line[1:11]:
name, value = entry.split('=')
try:
xbo[name] = int(value)
except ValueError:
xbo[name] = value[1:-1] # stripping of the ""
Now headercontains the extensions of your domain:
{'DX': 615.0, 'DY': 425.0, 'DZ': 22.15}
and xbo the other values:
{'D': 10,
'F': 1,
'N': 'P',
'R': 0,
'V': 1000,
'X': 100,
'Y': 50,
'Z': 5,
'x': 0,
'y': 0}
Access the individual values in the dictionaries:
>>> header['DX']
615.0

Accessing dictionary key with multiple values

I have an assignment and for the first part, I am to access a text file which will have a list of production rules. I created a list of dictionaries from this text file:
x = y
x = y x
y = 0
y = 1
that looks like this:
myList = [{'x': 'y'}, {'x':'y x'}, {'y': 0}, {'y': 1}]
I want to find all the possible outputs when applying these rules. I am going to attempt to write code later that will go through and replace the nonterminal values and output a bunch of binary. However, for this dictionary:
{'x': 'y x'}
'y x' is all one string so I cannot replace y or x with anything unless I explicitly say
'y x' = some value
I have written this code and written a really bad test code to see if the computer can see if a value for a key exists:
prodList = []
for line in open('name of file', "r"):
line = line.strip()
lhs, rhs = line.split(' = ')
myList.append({lhs:rhs})
if 'y' in myList[0].values():
print True
Now if I run this it will print True and I could move on, but I can't seem to write code where if I wrote:
if 'y' in myList[1].values():
print True
that it would be True.
I tried writing
myList.append({lhs:rhs.split()})
But that didn't help and I couldn't check for any values at all. Is there any way that I could have the list look like this:
myList = [{'x': 'y'}, {'x':'y', x'}, {'y': 0}, {'y': 1}]
So that if I wrote
if 'y' in myList[1].values():
print True
it would return True?
If this sounds confusing, please let me know so I can try to clarify more.
I also tried to make a dictionary instead of a list of dictionaries by doing this:
for line in open('file.txt', "r"):
line = line.strip()
lhs, rhs = line.split(' = ')
myDict[lhs] = rhs
but when I printed the dictionary, I only got this:
{'y': 1, 'x': 'x y'}
I'm sure there is a better way to do this but I can't seem to figure out a way that works.
I looked over the above code again and I was just looking at the list of values and not the values themselves.
My question now is how do I make the dictionary with multiple values for one key? When I run this code:
for line in open(fileName, "r"):
line = line.strip()
lhs, rhs = line.split(' = ')
prodList[lhs] = rhs.split()
print prodList
I end up with just this:
{'y': [1], 'x': ['y', 'x']}
I'm not sure as to how I get this
myList = {'x': ['y'], 'x':['y', 'x'], 'y': [0], 'y':[1]}

Categories