Python3 Class Syntax Error: non-default argument follows default argument - python-3.x

I'm messing around with Class' and I have a python3 file with the following code:
class playerstats(object):
def __init__(self, name, baselevel=1, hp, mana, basedmg=0, atkdmg, basedef=0, chardef):
self.name = name
self.baselevel = baselevel
self.hp = hp
self.mana = mana
self.basedmg = basedmg
self.atkdmg = atkdmg
self.basedef = basedef
self.chardef = chardef
return self.name, self.baselevel, self.basedmg, self.basedef
def selectedclass(self, chosenclass):
if chosenclass == 'W' or chosenclass == 'w':
self.hp = 100
self.mana = 50
elif chosenclass == 'M' or chosenclass == 'm':
self.hp = 75
self.mana = 100
else:
print('Error')
return self.hp, self.mana
charcreation = playerstats('Tom', baselevel, self.chosenclass, self.chosenclass, basedmg, 0, basedef, 0)
self.chosenclass = 'w'
print(playerstats.hp)
When I run it, I get this Error:
File "..\Playground\", line 2
def init(self, name, baselevel=1, hp, mana, basedmg=0, atkdmg, basedef=0, chardef):
^
SyntaxError: non-default argument follows default argument
Can someone help me understand why?

You need to change the order of parameters in __init__ function.
Generally, non-default argument should not follow default argument , it means you can't define (a="b",c) in function.
The order of defining parameter in function are :
positional parameter or non-default parameter i.e (a,b,c)
keyword parameter or default parameter i.e (a="bcd",r="jef")
keyword-only parameter i.e (*args)
var-keyword parameter i.e (**kwargs)

Related

Why am I getting this error? AttributeError: type object 'bubbleSort' has no attribute 'array'

I'm trying to make my own bubble-sort algorithm for learning purposes. I'm doing it by:
Making a random array
Checking if the first two indexes of the array need to be swapped
it does this throughout the whole list
and does it over and over until when looping through until the end it doesn't need to swap anything anymore then the loop breaks
but when I print any variable in the class it says that the class has no attribute of the variable.
this is my code right now
from random import randint
class bubbleSort:
def __init__(self, size):
self.size = size # Array size
self.array = [] # Random array
self.sorted = self.array # Sorted array
self.random = 0 # Random number
self.count = 0
self.done = False
self.equal = 0
while self.count != self.size:
random = randint(1, self.size)
if random in self.array:
pass
else:
self.array.append(random)
self.count += 1
def sort(self):
while self.done != True:
self.equal = False
for i in range(self.size):
if i == self.size:
pass
else:
if self.sorted[i] > [self.tmp]:
self.equal += 1
if self.equal == self.size:
self.done = True
else:
self.sorted[i], self.sorted[i + 1] = self.sorted[i+1], self.sorted[i]
new = bubbleSort(10)
print(bubbleSort.array)
This is what outputs
Traceback (most recent call last):
File "/home/musab/Documents/Sorting Algorithms/Bubble sort.py", line 38, in <module>
print(bubbleSort.array)
AttributeError: type object 'bubbleSort' has no attribute 'array'
In your case, you have a class called bubbleSort and an instance of this class called new, which you create using new = bubbleSort(10).
Since bubbleSort only refers to the class itself, it has no knowledge of member fields of any particular instance (the fields you create using self.xyz = abc inside of the class functions. And this is good, imagine having two instances
b1 = bubbleSort(10)
b2 = bubbleSort(20)
and you want to access the array of b1, you need to specify this somehow. The way to do it is to call b1.array.
Therefore, in your case you need to print(new.array).
bubbleSort is a class type, each object of this class type has its own array. To access array, one must do it through a class object. __init__ is called when creating a class object.
give the following a try:
bubbleSortObj = bubbleSort(10) # create a bubbleSort object
print(bubbleSortObj.array) # print the array before sort
bubbleSortObj.sort() # sort the array
print(bubbleSortObj.array) # print the array after sort
Notes
In __init__ you've got:
self.array = [] # Random array
self.sorted = self.array # Sorted array
In this case, array and sorted point to the same list and changing one would change the other. To make a copy of a list, one approach (among many) is to call sorted = list(array)
If there are any local function variables you can remove the self, eg, self.count = 0 can just be count = 0, as it's not needed again once it's used, and doesn't need to be a class member

Create a python Class inheriting from a Class created by a function

What works :
I am using the module recordtype to store global parameters for the different functions of my program.
Each parameter is a class instance of :
class Parameter(object):
def __init__(self, name, value, info):
self.name = name
self.value = value
self.info = info
Then the global structure is defined like this :
The individual parameters :
parameter_1 = Parameter('param_1', 10, 'Value for the parameter 1, usage...')
parameter_2 = Parameter('param_2', 20, 'Value for the parameter 2, usage...')
...
parameter_m = Parameter('param_n', 50, 'Value for the parameter m, usage...')
parameter_n = Parameter('param_n', 100, 'Value for the parameter n, usage...')
Parameter sub sets :
parameter_set_1 = recordtype('parameter_set_1', [(parameter_1.name, parameter_1),
(parameter_2.name, parameter_2), ...])
...
parameter_set_n = recordtype('parameter_set_n', [(parameter_m.name, parameter_m),
(parameter_n.name, parameter_n)]
The global parameter structure is then :
GlobalParametersFunction = recordtype('GlobalParameters', [('parameter_set_1', parameter_set_1()),
('parameter_set_2', parameter_set_2()), ('parameter_set_n', parameter_set_n())])
Which needs to be instantiated :
GlobalParameters = GlobalParameterFunction()
This all works well, the GlobalParameterFunction creates a class where I can access individual parameters and change their values, Ex:
GlobalParameters.parameter_set_1.parameter_1.value = 20
From the GlobalParameters class instance I can then make a function that prints the values and their names :
def print_parameter_info(GP):
for field, val in zip(GP._asdict(), GP._asdict().values()):
print(field, ':')
for key, entry in zip(val._asdict(), val._asdict().values()):
print('\t', entry.name, ':', entry.value)
Which does a nice print for the user :
>>> print_parameter_info(GlobalParameters)
parameter_set_1 :
parameter_1 : 10
parameter_2 : 20
parameter_set_n :
parameter_m : 50
parameter_n : 100
I would also like to create a function such that :
change(GlobalParameters, 'name', new_value)
does :
GlobalParameters.parameter_set_1.name.value = new_value
Which seems easily doable with the class created by recordtype
The problem :
I would like to create a class methods for the GlobalParameters class instance, from the print_parameter_info() function such that :
GlobalParameters.print_parameter_info()
and
GlobalParameters.change(name, new_value)
works
because GlobalParameters is a class instance of recordtype, I tried :
class GlobalParameterClass(recordtype):
def __init__(self):
self = GlobalParameterFunction()
But because recordtype is a function creating a class ?
I get the following error :
TypeError: function() argument 'code' must be code, not str
I found this question (2231427)
But after trying to import the right thing and looking at the source code of recordtype I think I understood that recordtype does not define a class clearly and creates it by parsing a string of code ?
Thus I do not understand how to create a class inheriting from the class created by recordtype
I also tried
class GlobalParameterClass(object):
def __init__(self, *args):
self = GlobalParameterFunction(*args)
This does not raise any errors but the class instance created is empty.
TLDR/Conclusion
How can I add custom methods to the class created by the recordtype function from the recordtype module ?
Or
Perhaps, there is there a better way to manage the GlobalParameters object ?
Thank you !
I found a solution without using the recordtype object, by creating a custom classes with the desired behavior :
class Parameter(object):
"""
A class to store the individual sound parameters
"""
def __init__(self, name, value, info):
self.name = name
self.value = value
self.info = info
class ParameterSet(object):
"""
A class to store multiple parameters as a set
"""
def __init__(self, name, *parameters):
self.name = name
for parameter in parameters:
setattr(self, parameter.name, parameter)
class GlobalParameters(object):
"""
A class to store the parameter sets and to be used to assign parameter values to the different functions
"""
def __init__(self, *parameter_sets):
for parameter_set in parameter_sets:
setattr(self, parameter_set.name, parameter_set)
The methods of the GlobalParameters classes go as follow :
def info(self):
for parameter_set in self.__dict__.values():
print(parameter_set.name)
parameters = [parameter for parameter in parameter_set_1.__dict__.values() if type(parameter) != str]
for parameter in parameters:
print('\t', parameter.name, ':', parameter.value)
def change(self, name, value):
for parameter_set in self.__dict__.values():
parameters = [parameter for parameter in parameter_set_1.__dict__.values() if type(parameter) != str]
for parameter in parameters:
if parameter.name == name:
parameter.value = value
With this example code :
# Individual parameters
parameter_1 = Parameter('parameter_1', 10, 'Value for the parameter 1, usage...')
parameter_2 = Parameter('parameter_2', 20, 'Value for the parameter 2, usage...')
parameter_n = Parameter('parameter_n', 50, 'Value for the parameter n, usage...')
parameter_m = Parameter('parameter_m', 100, 'Value for the parameter m, usage...')
# Parameter sets
parameter_set_1 = ParameterSet('parameter_set_1', parameter_1, parameter_2)
parameter_set_2 = ParameterSet('parameter_set_n', parameter_n, parameter_m)
# Global parameter set
GP = GlobalParameters(parameter_set_1, parameter_set_2)
I get the desired behavior :
>>> GP.info()
parameter_set_1
parameter_1 : 10
parameter_2 : 20
parameter_set_n
parameter_1 : 10
parameter_2 : 20
As well as with the .change method :
>>> GP.parameter_set_1.parameter_1.value
10
GP.change('parameter_1', 15)
>>> GP.parameter_set_1.parameter_1.value
15

how to pass expression as argument to a function without evaluating it

First, see the relevant user code for running z3-solver. This works fine, and the solver returns some values after evaluating the expression:
# see source at https://github.com/Z3Prover/z3
# the relevant code is at: z3-master/src/api/python/z3/z3.py
# pip install z3-solver
from z3 import *
x = Int('x')
#print(type(x)) # <class 'z3.z3.ArithRef'>
y = Int('y')
s = Solver()
# print(type(s)) # <class 'z3.z3.Solver'>
s.add(x > 10) # this works
s.add(y == x + 2)
print(s)
print(s.check())
print(s.model())
Now, my short attempt at writing an evaluator like z3:
class Int():
def __init__(self,name):
self.name = name
self.value = None
def do_print(self):
print('Rand({})'.format(self.name))
def __str__(self):
return(str(self.value))
class Solver():
def add(self, *argv):
self.args = []
for arg in argv:
self.args.append(arg)
def do_print(self):
print('Constr(',end='')
for arg in self.args:
print(arg)
print(')')
x = Int('x')
s=Solver()
s.add(x > 10) # this does not work - but why is same kind of code in z3 working?
# err: TypeError: '<' not supported between instances of 'Int' and 'int'
x.do_print()
s.do_print()
I am getting an error on the s.add() line. I understand the error, that the object 'x' cannot be compared with an integer. This is happening because the expression in this line is getting evaluated before the s.add() is being called.
But my question is - how is z3-solver bypassing this error? How is it able to pass the args to add() without evaluating it first?
How do I fix the problem in my code, and pass the expression to s.add() without evaluating it first?

Can't change instance attribute of my class?

I'm starting out in python and I can't quite figure out why I'm unable to change the data stored in one of my instance attributes. I have the following code:
class Bank:
def __init__(self, bank = 'Unnamed'):
self.bank = bank
self.clients = []
self.status = {'bank': self.bank, 'clients': self.clients}
self.c_counter=0
def deposit(self, name = None, amount = 200):
self.name = name
self.amount = amount
self.c_counter += 1
if self.name is None:
self.name = print("client" + str(self.c_counter));
self.clients.append((self.name, self.amount))
else:
self.clients.append((self.name, self.amount))
This produces the following output:
bb = Bank("bb")
bb.deposit(amount = 2000)
bb.status
out: {'bank': bb, 'clients': [(None, 2000)]}
While the desired output is:
out: {'bank': bb, 'clients': [('client1', 2000)]}
As you can see, what I'm trying to do is to set the client name to "clientx" if client name is not specified during a deposit; the x is just a number to distinguish each generic client from other generic clients without a specific name.
If a specific name is not provided when calling the deposit attribute of the bank the client name is set to None by default. I check this with the if condition and change the client name accordingly, but for some reason the client name literally gets added as "None" to the list of clients in the dictionary (list of tuples). What is wrong here?
print prints a string to sys.stdout and returns None. you should change the line
self.name = print("client" + str(self.c_counter));
to
self.name = "client" + str(self.c_counter)
or maybe self.name = f"client{self.c_counter}" for python >= 3.6.
print is a NoneType, it's None and you can prove it:
>>> type(print())
<class 'NoneType'>
>>>
And:
>>> print(print())
None
>>>
So print isn't be used for assigning, print is used for outputting stuff, whereas in this case you're assigning stuff, that which isn't meant for print to handle, so change:
self.name = print("client" + str(self.c_counter));
To:
self.name = "client" + str(self.c_counter)
Or:
self.name = "client%s" % self.c_counter
Or:
self.name = "client{}".format(self.c_counter)
Or if your python version is bigger or equal to 3.6, you can use:
self.name = f"client{self.c_counter}"

I am getting the error : ValueError: not enough values to unpack (expected 2, got 1). What could be the problem?

Im running a mapreduce job, but it keeps failing saying missing input. Unfortunately its not showing where its missing either
from mrjob.job import MRJob
from mrjob.step import MRStep
import re
class flight_combination(MRJob):
def steps(self):
return [MRStep(mapper=self.mapper_1,reducer=self.reducer_1)]
def mapper_1(self,_,value):
group1 = {}
group2 = {}
parts = value.split(",")
destination = parts[0]
origin = parts[1]
count = parts[2]
group1[destination] = {'Origin': origin, 'count': count}
group2[origin] = {'Destination':destination,'count':count}
yield group1
yield group2
def reducer_1(self,key,value):
g1,g2 = data
for key1 in g1:
for key2 in g2:
if g1[key1]['Origin'] == g2[key2]['Destination']:
total = int(g1[key1]['count'])*int(g2[key2]['count'])
yield (key1,key2,total)
if __name__ == '__main__':
flight_combination.run()
Following is the error:
`File "wd.py", line 35, in <module>
flight_combination.run()
…...
File "/usr/lib/python3.6/site-packages/mrjob/job.py", line 536, in run_mapper
for out_key, out_value in mapper(key, value) or ():
ValueError: not enough values to unpack (expected 2, got 1)`
The run method of the object type flight_combination is expecting 2 arguments, but provided with 1 argument. ( Python by default takes self as the first argument for methods invoked on object)
To fix this -
as the method run is defined in the parent class, go through its definition and pass the other argument.
Override the run method by redefining it flight_combination class and provide your logic.

Resources