Self in Init Params

Self in init params

Instead of using Self or A in each of the initialisers, you can simply override each subclass' initialiser to use its own type as operation.

This works because A's initialiser states that operation should be a type that conforms to A, and when you override it you have the liberty to use a subclass of A as operation instead. However, if you change operation to an unrelated type such as String or Int, the compiler will not override the existing initialiser.

Firstly, define A with its init:

class A {
init(finishBlock: ((_ operation: A) -> Void)?) {...}
}

Now to create a subclass, you must override init using the subclass' type as operation instead. In your call to super.init, force upcast operation ($0) to your subclass' type, and call finishBlock with this casted operation.

class B: A {
override init(finishBlock: ((_ operation: B) -> Void)?) {
// Perform custom initialisation...
super.init { finishBlock?($0 as! B) }
}

func fooOnlyInB() {
print("foo")
}
}

B's initialiser now passes B as operation, which means that you don't need to cast it yourself anymore! This is thanks to the fact that you can override an init with a more specific type, in this case B.

let b = B { operation in
operation.fooOnlyInB() // prints "foo"
}

Using self in init part of a class in Python

No. there is no difference between these two approaches in your case with this level of information. but could they? Yes. they could. if they have some modifications in their setters or getters. later in my answer I'll show you how.

First of all, I prefer using this one:

class summation: 
def __init__(self, f, s):
self.first = f
self.second = s
@property
def summ(self):
return self.first+self.second

the above implementation calculates the summation on demand. so when you change self.first or self.second, summ will be calculated automatically. you can access the sum as you did before.

s = summation(1,9)
print(s.summ)
# 10
s.first = 2
s.second = 3
print(s.summ)
# 5

So, How could they be different?

let's implements them as follows. in setters I doubled the inputs to show you how setters can affect the results. it's just an imaginary example and is not exactly what you wrote.

class summation1: 
def __init__(self, f, s):
self.first = f
self.second = s
self.summ = self.first + self.second

@property
def first(self):
return self.__first

@first.setter
def first(self,f):
self.__first = f*2

@property
def second(self):
return self.__second

@second.setter
def second(self,s):
self.__second = s*2


class summation2:
def __init__(self, f, s):
self.first = f
self.second = s
self.summ = f + s

@property
def first(self):
return self.__first

@first.setter
def first(self,f):
self.__first = f*2

@property
def second(self):
return self.__second

@second.setter
def second(self,s):
self.__second = s*2

now let's take a look at the outputs:

a = 3
b = 2
s1 = summation1(a,b)
s2 = summation2(a,b)

print(s1.summ)
# 10
print(s2.summ)
# 5

so, if you are not sure what to choose between those two, maybe the first approach is what you need.

How to get class of object being created in __init__ parameters

I think that you should put it in the body, not the parameter.

def __init__(self, foo):
self.foo = foo
self.bar = type(self).some_class_variable
self.ham = self.some_function(type(self).some_other_class_variable)

EDIT:
If the values are defaults, you can do this:

default_value = 'default pls'
def __init__(self, foo, bar=default_value, ham=default_value):
self.foo = foo
if default_value == bar:
self.bar = type(self).some_class_variable
if default_value == ham:
self.ham = self.some_function(type(self).some_other_class_variable)

Adding parameters to init and create instance

You never define self.speed_top_border, self.speed_bottom_border in your __init__, but try to use them in self.obstacleX_change = random.uniform(self.speed_top_border, self.speed_bottom_border)

If you want to have these attributes - define them, before using them

class Obstacle(object):

def __init__(self, speed_top_border, speed_bottom_border):
self.obstacleImg = 'rock.png'
self.obstacleX = random.randint(700, 800)
self.obstacleY = random.randint(0, ScreenHeight - 64)
self.speed_top_border = speed_top_border
self.speed_bottom_border = speed_bottom_border
self.obstacleX_change = random.uniform(self.speed_top_border, self.speed_bottom_border)

Python Class __init__ Confirmation

Firstly: The __init__() function is special in that it is called for you while creating a new instance of a class. Apart from that, it is a function like any other function, you can even call it manually if you want.

Then: You are free to do what you want with the parameters passed to a function. Often, the parameters passed to __init__() are stored inside the object (self) that is being initialized. Sometimes, they are used in other ways and the result then stored in self, like passing a hostname and using that to create a socket - you then only store the socket. You don't have to do anything with the parameters though, you can ignore them like you do in your First() function, which receives a parameter name which is ignored. Note that this parameter name and the similar attribute self.name are different things!

Notes:

  • It is uncommon to ignore parameters passed to __init__() though, just as it is uncommon to ignore parameters in general. In some cases, they are "reserved" so they can be used by derived classes but typically they are unintended (as with your First() function, I guess).
  • Check out PEP8, which is a style guide. Adhering to it will make it easier for others to read your code. E.g. First() should be first() instead.
  • Upgrade to Python 3. I don't think there's any excuse nowadays to learn Python 2 and littley excuse except maintenance to use it in general.

Python class's init function takes instance of own class as default parameter

You should do this:

class Node:
def __init__(self, name, visited=False, distance=math.inf, path=None):
self.name = name
self.visited = visited
self.distance = distance
if path is None:
self.path = Node('-', path="default-path")
else:
self.path = path

This is the idiom you should be following with mutable default arguments to begin with.

However, you need the default to have a path, or it will recurse without stopping.

Why isn't my class initialized by def __int__ or def _init_ ? Why do I get a takes no arguments TypeError, or an AttributeError?

What do the exception messages mean, and how do they relate to the problem?

As one might guess, a TypeError is an Error that has to do with the Type of something. In this case, the meaning is a bit strained: Python also uses this error type for function calls where the arguments (the things you put in between () in order to call a function, class constructor or other "callable") cannot be properly assigned to the parameters (the things you put between () when writing a function using the def syntax).

In the examples where a TypeError occurs, the class constructor for Example does not take arguments. Why? Because it is using the base object constructor, which does not take arguments. That is just following the normal rules of inheritance: there is no __init__ defined locally, so the one from the superclass - in this case, object - is used.

Similarly, an AttributeError is an Error that has to do with the Attributes of something. This is quite straightforward: the instance of Example doesn't have any .attribute attribute, because the constructor (which, again, comes from object due to the typo) did not set one.

Why didn't a problem occur earlier, for example, with the class definition itself?

Because the method with a wrongly typed name is still syntactically valid. Only syntax errors (reported as SyntaxError; yes, it's an exception, and yes, there are valid uses for it in real programs) can be caught before the code runs. Python does not assign any special meaning to methods named _init_ (with one underscore on each side), so it does not care what the parameters are. While __int__ is used for converting instances of the class to integer, and shouldn't have any parameters besides self, it is still syntactically valid.

Your IDE might be able to warn you about an __int__ method that takes suspicious parameters (i.e., anything besides self). However, a) that doesn't completely solve the problem (see below), and b) the IDE might have helped you get it wrong in the first place (by making a bad autocomplete suggestion).

The _init_ typo seems to be much less common nowadays. My guess is that people used to do this after reading example code out of books with poor typesetting.

How else might the problem manifest?

In the case where an instance is successfully created (but not properly initialized), any kind of problem could potentially happen later (depending on why proper initialization was needed). For example:

BOMB_IS_SET = True
class DefusalExpert():
def __int__(self):
global BOMB_IS_SET
BOMB_IS_SET = False
def congratulate(self):
global BOMB_IS_SET
if BOMB_IS_SET:
raise RuntimeError("everything blew up, gg")
else:
print("hooray!")

If you intend for the class to be convertible to integer and also wrote __int__ deliberately, the last one will take precedence:

class LoneliestNumber:
def __int__(self):
return 1
def __int__(self): # was supposed to be __init__
self.two = "can be as bad"

>>> int(LoneliestNumber())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __int__ returned non-int (type NoneType)

(Note that __int__ will not be used implicitly to convert instances of the class to an index for a list or tuple. That's done by __index__.)

How might I guard against the problem in the future?

There is no magic bullet. I find it helps a little to have the convention of always putting __init__ (and/or __new__) as the first method in a class, if the class needs one. However, there is no substitute for proofreading, or for training.



Related Topics



Leave a reply



Submit