Related
I have spent the past few hours reading around but I'm not really understanding what I am sure is a very basic concept: passing values (as variables) between different functions.
class BinSearch:
def __init__(self,length,leng,r,obj_function,middle):
self.length = length
self.leng = leng
self.r = r
self.obj_function = obj_function
self.middle = middle
self.objtobin(obj_function)
def BinarySearch(length,leng,r):
mid = np.arange(0,len(length),1)
middle = min(mid) + (max(mid)-min(mid))//2
L_size = []
L = length[middle]
L_size.append(L)
return L
def objtobin(self,obj_function):
# length,leng,middle = BinSearch.BinarySearch()
if (obj_function>=0.98):
return BinSearch.BinarySearch(self.length,min(leng),self.middle-1)
else:
return BinSearch.BinarySearch(self.length,self.middle+1,max(leng))
BinSearch.objtobin(obj_function=max(objectivelist))
When I run the above code, BinSearch.objtobin code gives "objtobin() missing 1 required positional argument: 'self'" What should I do for this error?
Thanks for help!
Firstly, thank you all for your help. But I do not understand how should I change this code
I have started modifying your code so that it would run without errors, but there are a few other mistakes in there as well, and I have not tried to make sense of all your parameters.
It would look something like this, but I will explain below.
# --- OP's attempt that fails ---
# BinSearch.objtobin(obj_function=max(objectivelist))
# -- -- -- -- -- -- -- -- -- -- --
# --- Using an instance ---
figure_this_out_yourself = 100
# this variable is a placeholder for any parameters I had to supply
myBinSearchInstance = BinSearch(
length = figure_this_out_yourself,
leng = [figure_this_out_yourself],
r = figure_this_out_yourself,
obj_function = figure_this_out_yourself,
middle = figure_this_out_yourself)
myBinSearchInstance.objtobin(obj_function = max(objectivelist))
There is one important concept to be grasped here: self.
Let us consider this simple example function here, which shall always output a number one larger than last time.
counter = 0
def our_function ():
global counter
counter = counter + 1
return counter
print(our_function())
It is okay as it is, but it uses a global variable to keep track of its state. Imagine using it for two different purposes at the same time. That would be chaos!
So instead, we package this inside a class.
# unfinished apprach
counter = 0
class OurClass:
# This is called a static method
def our_function ():
global counter
counter = counter + 1
return counter
print(our_function())
When we try to run this, we run into a problem.
NameError: name our_function is not defined
This happens because it is now accessible only within that class. So we need to call it as
print(OurClass.our_function())
That makes it okay to have functions with the same name around - as long as they are in different classes - but it does not solve our chaos for using our_function multiple times at once. What we want is basically to have two independent counter variables. This is where instances come into play: Of course we could manually create a second function that uses a second global variable, but that gets out of hand quickly when you use it more and more.
So let's move counter inside our class.
class OurClass:
counter = 0
def our_function ():
global counter
counter = counter + 1
return counter
You guessed it - now counter is no longer defined:
NameError: name counter is not defined
So let us pass the instance variable that we want to use into the function as a parameter. And then use that instance to get its counter:
class OurClass:
counter = 0
def our_function (the_instance):
the_instance.counter = the_instance.counter + 1
return the_instance.counter
myInstance = OurClass()
mySecondInstance = OurClass()
print(OurClass.our_function(myInstance))
print(OurClass.our_function(mySecondInstance))
And successfully, both print statements print 1!
But that is a bit annoying because this the_instance is something that is not like the other arguments. To make it distinct, python allows us to avoid the first parameter and instead provide it as the receiver. Both of these work:
print(myInstance.our_function())
print(OurClass.our_function(mySecondInstance))
Python uses a very strong convention for these parameters. Instead of the_instance, call it self. See Why is self only a convention?.
class OurClass:
counter = 0
def our_function (self):
self.counter = self.counter + 1
return self.counter
myInstance = OurClass()
mySecondInstance = OurClass()
print(myInstance.our_function())
print(mySecondInstance.our_function())
Now we're almost done! Just one thing left to understand: Where do the parameters of __init__() come from?
They are passed to __init__() from the line where we construct it. So let me demonstrate by adding a starting value for our counter:
class OurClass:
counter = 0
def __init__ (self, starting_value):
self.counter = starting_value
def our_function (self):
self.counter = self.counter + 1
return self.counter
myInstance = OurClass(5)
mySecondInstance = OurClass(10)
print(myInstance.our_function())
print(OurClass.our_function(mySecondInstance))
This prints 6 and 11.
But what do those comments mean with #staticmethod? For that, see Difference between staticmethod and classmethod and Do we really need #staticmethod decorator in python to declare static method
.
In short: You can annotate any method in a class with either #staticmethod or #classmethod.
#staticmethod means that it can be called like myInstance.foo() when OurClass.foo() does not take self as a parameter. Without that decorator, you could only call it as OurClass.foo() but not as myInstance.foo().
#classmethod means that it can be called like myInstance.foo() and it does not get myInstance as the first parameter, but instead the class of myInstance, which is OurClass. That allows you e.g. to define alternative constructors. Also, a class method is not inherited when you subclass it, so it won't be mistakenly called.
The comments are pointing out that you could also use a #staticmethod and avoid creating an instance. For that, you would have to not use any variables in the class itself - but you aren't using those for long anyways, so you could all pass them as parameter to the function.
I don't think the design below is a good idea. However, this is for an exam, and they require a list (technically an array, because Cambridge is very biased in favour of VB and Pascal) where each element is of a certain class type. Here is the class diagram:
Also, the board demands that all attributes be private even though -- based on my rudimentary knowledge of Python -- "private" attributes aren't really a thing. Here is the code exhibiting the problem:
class Question():
def __init__(self):
self.__QuestionID = ""
self.__QuestionText = ""
self.__Answer = ""
self.__Mark = ""
self.__Topic = ""
class Test():
def __init__(self):
self.__TestdID = ""
self.__Questions = []
self.__NumOfQs = None
self.__MaxMarks = 0
self.__Level = None
self.__DateSet = None
def DesignTest(self):
self.__NumOfQs = int(self.__NumOfQs)
self.__Questions = [Question for x in range(self.__NumOfQs)] `
for i in self.__Questions:
i = Question()
i.SetQuestion()
The last four lines are my problem area: I don't know how to implement the list of instance objects.
You are correct: Python doesn't have explicit private and public attributes.
I'm not sure what you're trying to do in the final for loop: you already have a list, except that you filled it with the class object itself, instead of instances. Try this:
self.__Questions = [Question() for x in range(self.__NumOfQs)]
Now you have a list of instance objects. If you need to call SetQuestion on each, you already have it set up in that loop:
for i in self.__Questions:
i.SetQuestion()
You can just initiate them in the list itself.
use
self.__Questions = [Question() for x in range(self.__NumOfQs)]
then also try :
for i in self.__Questions:
i.SetQuestion()
The line
self.__Questions = [Question for x in range(self.__NumOfQs)]
will create a list with uninstanciated objects, as many as you have in self.__NumOfQs. If you want to create instanciated objects, just do
self.__Questions = [Question() for x in range(self.__NumOfQs)]
instead and skip the last lines, or skip the line above and do
for i in self.__NumOfQs:
self.__Questions.append(Question())
If i understand correctly you just want a list containing instances of the Question class? You can simply instantiate them in the list generation.
self.__Questions = [Question() for x in range(self.__NumOfQs)]
You can then finish with:
for i in self.__Questions:
i.SetQuestion()
I need help on creating an object (a sequence of numbers) in respect to some parameters of a class. Lets say I typed in to the Python IDLE shell:
SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
#means user can create a 'SuperLotto' with 6 numbers in range of 1 to 50
It would make 'SuperLotto' as a new class instance of a class called 'LotteryGameType'.
This is using the code so far:
class LotterySetError(Exception):
pass
def make_lottery_set_type(name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
name = LotteryGameType(name, size, minmax[0], minmax[1])
return name
class LotteryGameType:
def __init__(self, name, set_size, min_set_number, max_set_number):
self.name = name
self.set_size = set_size
self.min_set_number = min_set_number
self.max_set_number = max_set_number
I want to be able to create a sequence of numbers and storing it for later use so I can use it with things like overload operators (e.g. eq and ne).
I want to be able to type into the Python IDLE shell:
SuperLotto([3, 4, 19, 23, 46, 27])
This would create an object under the parameters of SuperLotto, if not under parameters of 'SuperLotto' (say more than 6 numbers), it would raise an error. Any approach would be fine. Does anyone have any ideas on how to approach this?
It sounds like what you want is for make_lottery_set_type to return a new class, presumably one that's a subclass of LotteryGameType, rather than returning an instance of that type.
This is actually pretty easy to do in Python. Class definitions are just normal code, that you can run anywhere, even in the middle of a function. And they have access to the local environment while they're running. And classes themselves are "first-class values", meaning you can pass them around and return them from functions. So:
def make_lottery_set_type(name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
class NewLotteryGameType(LotteryGameType):
def __init__(self, numbers):
super().__init__(name, size, minmax[0], minmax[1])
self.numbers = numbers
return NewLotteryGameType
If you want to add other methods, that's the same as adding methods to any other class. For example:
def make_lottery_set_type(name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
class NewLotteryGameType(LotteryGameType):
def __init__(self, numbers):
super().__init__(name, size, minmax[0], minmax[1])
self.numbers = numbers
def __eq__(self, rhs):
return set(self.numbers) == set(rhs.numbers)
return NewLotteryGameType
So:
>>> SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
>>> super1 = SuperLotto([1,2,3,4,5,6])
>>> super2 = SuperLotto([6,5,4,3,2,1])
>>> super3 = SuperLotto([7,8,9,10,11,12])
>>> super1 == super2
True
>>> super1 == super3
False
(Obviously you can define __eq__ however you want, if set-equality isn't the right rule for your use.)
If you try to inspect the values you're generating, they don't look quite as pretty as you might like. For example, you'd probably rather see SuperLotto rather than NewLotteryGameType in places like this:
>>> super1
<__main__.NewLotteryGameType at 0x10259e490>
>>> SuperLotto.__name__
'NewLotteryGameType'
For that, just add NewLotteryGameType.__name__ = name. You might also want to copy over the docstring from the parent class, or various other things.
More generally, look at functools.update_wrapper (which is designed for wrapping up functions, not classes, but many of the details are the same) for inspiration, and the inspect module docs from your Python version for all of the attributes that classes can have.
In a comment, you ask:
The only problem is that I want NewLotteryGameType to inherit the parameters such as name, set_size, min_set_number, max_set_number from LotteryGameType. So lets say I wanted to type in NewLotteryGameType.set_size in to the Python Shell. I want it to return back to me 6.
That's contradictory. If you want to inherit the instance attributes of LotteryGameType… well, you already do. For example:
>>> super1.set_size
6
If you want them to be accessible off the class, then they can't be instance attributes, they have to be class attributes. And just changing set_size to a class attribute of LotteryGameType and inheriting it won't work, because the whole point of a class attribute is that the same value shared by all instances of the class or any of its subclasses, and the subclasses all need different values.
But you could do something like this:
class LotteryGameType:
def __init__(self, min_set_number, max_set_number):
self.min_set_number = min_set_number
self.max_set_number = max_set_number
def make_lottery_set_type(lottery_name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
class NewLotteryGameType(LotteryGameType):
name = lottery_name
set_size = size
def __init__(self, numbers):
super().__init__(minmax[0], minmax[1])
self.numbers = numbers
def __eq__(self, rhs):
return set(self.numbers) == set(rhs.numbers)
return NewLotteryGameType
(Notice that I had to rename the first make_ parameter to lottery_name so it was different from the class attribute name, because of the way scopes work.) Now, name and set_size are not instance attributes, nor are they class attributes of LotteryGameType—but they're class attributes of each NewLotteryGameType. So:
>>> SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
>>> SuperDuperLotto = make_lottery_set_type('SuperDuperLotto', 8, (1,100))
>>> SuperLotto.set_size
6
>>> SuperDuperLotto.set_size
8
What if you create instances of those types? Well, Python looks for attributes in the instance, then in the most-derived class, and then the base classes. So as long as you don't create instance attributes with the same name (notice that I removed the extra params, and the code that set instance attributes, from the LotteryGameType.__init__ method), it does just what you'd want:
>>> super1 = SuperLotto([1,2,3,4,5,6])
>>> super1.set_size
6
>>> duper1 = SuperDuperLotto([1,2,3,4,5,6,7,8])
>>> duper1.set_size
8
Of course this means that LotteryGameType is no longer a usable type on its own; only its subclasses are usable. But that's probably what you wanted anyway, right? You could even consider making it explicitly an abstract base class to make sure nobody accidentally tries to use a direct LotteryGameType instance.
If you're feeling brave, you might want to read up on metaclasses and see how you could adapt this whole design into use a LotteryGameMetaclass, so each new class is an instance of that metaclass instead of a subclass of the (abstract) base class. The source for the new enum module in 3.4, or the near-equivalent external flufl.enum package, might make good sample code. Then you can play with both and see how similar and how different they are.
I have a class which represents an object to be kept in a set. I would like the class itself to remember how many it has created so that when you call SetObject() and __init__() a new object is created, which receives a unique index. Maybe something like this
class SetObject(object):
# static class variable
object_counter = 0
def __init__(self, params):
self.params=params
self.index = self.get_index()
def get_index(self):
object_counter += 1
return object_counter-1
a = SetObject(paramsa)
b = SetObject(paramsb)
print a.index
print b.index
would produce
0
1
or something like this. Currently it seems that this approach gives a "variable referenced before assignment" error.
You need to write:
def get_index(self):
SetObject.object_counter += 1
return SetObject.object_counter-1
otherwise it would only work if object_counter was a global variable.
You need to use a reference to the class to refer to it's variables; you could perhaps use a class method (with the #classmethod decorator), but there is really no need to.
Better use itertools.count() to get a fool-proof 'static' counter; no need to reassign back to the class attribute then:
import itertools
class SetObject(object):
object_counter = itertools.count().next
def __init__(self, params):
self.params=params
self.index = self.object_counter()
(code above assumes Python 2; on Python 3 iterables do not have a .next method and you'd need to use functools.partial(next, itertools.count()) instead).
Because the counter is an iterator, we don't need to assign to SetObject.object_counter at all. Subclasses can provide their own counter as needed, or re-use the parent class counter.
The line
object_counter += 1
translates to
object_counter = object_counter + 1
When you assign to a variable inside a scope (e.g. inside a function), Python assumes you wanted to create a new local variable. So it marks object_counter as being local, which means that when you try to get its value (to add one) you get a "not defined" error.
To fix it, tell Python where to look up object_counter. In general you can use the global or nonlocal keywords for this, but in your case you just want to look it up on the class:
self.__class__.object_counter += 1
Lets suppose this example: Two siblings classes where one loads the other class as a new attribute and then i wish to use this attribute from the main class inside the sibling.
a = 2
class AN(object):
def __init__(self,a):
self.aplus = a + 2
self.BECls = BE(a)
class BE(object):
def __init__(self,a):
print a
def get_aplus(self):
????
c = AN(a)
and i'd like to do:
c.BECls.get_aplus()
and this shall return something like self.self.aplus (metaphorically), that would be 4
Resuming: get aplus attribute from AN inside BE class, without declaring as arguments, but doing a "Reverse introspection", if it possible, considering the 'a' variable must be already loaded trough AN.
Sorry if I not made myself clear but I've tried to simplify what is happening with my real code.
I guess the problem may be the technique i'm using on the classes. But not sure what or how make it better.
Thanks
OP's question:
get aplus attribute from AN inside BE class, without declaring as
arguments, but doing a "Reverse introspection", if it possible,
considering the 'a' variable must be already loaded trough AN.
The closest thing we have to "reverse introspection" is a search through gc.getreferrers().
That said, it would be better to simply make the relationship explicit
class AN(object):
def __init__(self,a):
self.aplus = a + 2
self.BECls = BE(self, a)
class BE(object):
def __init__(self, an_obj, a):
self.an_obj = an_obj
print a
def get_aplus(self):
return self.an_obj.aplus
if __name__ == '__main__':
a = 2
c = AN(a)
print c.BECls.get_aplus() # this returns 4