not all attributes defined in __init__? - python

I've seen the following code.
class Primes:
def __init__(self, max):
self.max = max
self.number = 1
def __iter__(self):
return self
def __next__(self):
self.number += 1
if self.number >= self.max:
raise StopIteration
elif check_prime(self.number):
return self.number
else:
return self.__next__()
In the dunder init function, we set self.number=1, without having included earlier the attribute number. What's the meaning of it?

This code, only means that self.number is not customizable and will always values 1 when creating an instance of Primes. This is used when the class need an attribute, which will be used along its state and methods, but it's value should always be the same when instanciating the object
def __init__(self, max):
self.max = max
self.number = 1

Related

Can shorthand operators be used with getters and setters?

Is it possible to set a property with shorthand operators like +=, or -=?
I've read this post, What's the pythonic way to use getters and setters?, but it deals mainly with direct assignment in terms of object.attr = value. When thinking about it, I'm finding it difficult to be possible and I've never used setters so I'm not sure.
It is within the Player.hit() method where I'm attempting to achieve this where I'm subtracting and integer reduced_points from another integer value property:
self.hands[x].value -= reduced_points
NOTE: Hand.value is an #property due to that I need to dynamically add the values of each card in case a card is added to that hand.
class Card:
def __init__(self, pip, suit, *args, **kwargs):
self.pip = pip
self.suit = suit
if kwargs and 'face_card' in kwargs:
self.face_card = kwargs['face_card']
class Hand:
def __init__(self, cards):
self.cards = cards # [Card(), Card(), Card()]
self._value = sum(card.pip for card in cards)
#property
def soft(self):
return any(
hasattr(card, 'face_card') and
card.face_card == "Ace" for card in self.cards
)
#property
def value(self):
return sum([card.pip for card in self.cards])
#value.setter
def set_value(self, value):
new_value = self._value - value
self._value = new_value
class Player:
def __init__(self):
self.name = "Player"
self.hands = []
self.chips = 0
self._bet = 0
def hit(self, hand):
if hand.soft:
x, hand = list(filter(
lambda h: h[1].soft, enumerate(self.hands)
))[0]
total_aces = len(
list(
filter(lambda c: hasattr(c, 'face_card') and
c.face_card == "Ace", self.hands[x].cards)
)
)
reduced_points = 10 * (total_aces - 1)
self.hands[x].value -= reduced_points
if self.hands[x].value > 21:
return self.hands[x], "BUST"
if hand.value > 21:
return hand , "BUST"
return hand, "HIT"
When creating a property setter, it needs to have the same name as the getter. So both the getter and setter needed to be def value(...). But yes, hand.value -= 3 should do exactly what you expect it to.

Using property method with setter prevents function from binding to object attribute

I am attempting to use a property-based method to control the class constructor (screening for bad values at instance creation time), in this code. For some reason I don't understand the function set_sequence won't alter the derived class Binary object's attribute seq when called from the #num.setter method - it has to be called elsewhere. Am I missing something basic about how properties work?
class Number:
def __init__(self, num):
self.num = num
class Binary(Number):
def __init__(self, num):
super().__init__(num)
self.seq = ()
#property
def num(self):
return self._num
#num.setter
def num(self, value):
if value < 0:
raise ValueError('Unsigned numbers cannot be negative')
self._num = value
self.set_sequence() #this calls the function, but the value doesn't get bound
def set_sequence(self):
print('called ', end='')
self.seq = tuple([int(x) for x in bin(self.num)[2:]])
Calling this code as follows:
if __name__ == "__main__":
n1 = Binary(11)
print(f"{n1.num} = {n1.seq}")
n1.set_sequence()
print(f"{n1.num} = {n1.seq}")
Gives:
called 11 = ()
called 11 = (1, 0, 1, 1)
This throws an exception as expected when negative values are passed to constructor, but I don't understand why the function call fails to behave as expected. This pattern is based on Brett Slatkin's Item#29 in 'Effective Python' incidentally, the part about using #property to do type checking and value validation when the constructor is called.
Because in your constructor after super().__init__(num) that calls your #num.setter you use self.seq = () that overrides the value stored in your setter method.
To have the desired output, you should do like this. In you example self.set_sequence() is overridden by the second instruction in the constructor.
class Number:
def __init__(self, num):
self.num = num
class Binary(Number):
seq = ()
def __init__(self, num):
# or eventually here
# seq = ()
super().__init__(num)
#property
def num(self):
return self._num
#num.setter
def num(self, value):
if value < 0:
raise ValueError('Unsigned numbers cannot be negative')
self._num = value
self.set_sequence() #this calls the function, but the value doesn't get bound
def set_sequence(self):
print('called ', end='')
self.seq = tuple([int(x) for x in bin(self.num)[2:]])
if __name__ == "__main__":
n1 = Binary(11)
print(f"{n1.num} = {n1.seq}")
n1.set_sequence()
print(f"{n1.num} = {n1.seq}")

How to determine if a class was instantiated by a method in another class?

def _check_value(number):
if not 0 <= number <= 10:
raise ValueError
return number
class A:
def __init__(self, number):
self.number = _check_value(number)
class B:
def __init__(self):
self.factor = 2
def my_algorithm(self, number):
number = _check_value(number)
number += 1 * self.factor
return A(number)
The above example is a simplified version of my code. Class A can be instantiated directly or by the method my_algorithm() in class B. I need to check the number value in both cases. My problem is that when I instantiated class A using the method in class B, there will be a double checking for number value.
My question is how to know that class A was instantiated using the method in class B in order to avoid double checking? Or is there any better solution?
I am not 100% following your question on instances, but I reckon you don't have to check the value twice? Could try ... except be of help here?
def _check_value(number):
if not 0 <= number <= 10:
raise ValueError
return number
class A:
def __init__(self, number):
self.number = _check_value(number)
class B:
def __init__(self):
self.factor = 2
def my_algorithm(self, number):
try:
return A(number + 1 * self.factor)
except ValueError: # not a very descriptive error, but nbd
print("Provided value is out of bounds")
I ended up with following code to avoid double checking:
def _check_value(number):
if not 0 <= number <= 10:
raise ValueError
return number
class A:
def __init__(self, number, check=True):
if check:
self.number = _check_value(number)
else:
self.number = number
class B:
def __init__(self):
self.factor = 2
def my_algorithm(self, number):
number = _check_value(number)
number += 1 * self.factor
return A(number, check=False)

Using len() and def __len__(self): to build a class

Just curious,
Is there any difference (advantages and disadvantages) between using len() or def __len__() when I build a class? And which is the best Python style?
class foo(object):
def __init__(self,obs=[])
self.data = obs
self.max = max(obs)
self.min = min(obs)
self.len = len(obs)
or
class foo(object):
def __init__(self,obs=[])
self.data = obs
self.max = max(obs)
self.min = min(obs)
def __len__(self):
return len(self.data)
There is a huge difference.
The __len__() method is a hook method. The len() function will use the __len__ method if present to query your object for it's length.
The normal API people expect to use is the len() method, using a .len attribute instead would deviate from that norm.
If the length of self.data is not expected to change, you can always cache the length in an attribute and have .__len__() return that attribute.
class foo(object):
def __init__(self, obs=None):
if obs is None: # provide a default if no list was passed in.
obs = []
self.data = obs
self.max = max(obs)
self.min = min(obs)
self._data_len = len(obs)
def __len__(self):
return self._data_len
There are several differences:
Only the second approach will give you the familiar len(obj) syntax for foo. The first will require obj.len().
If the length of self.data can change post-construction, only the second version will reflect the new length.

python classes adding numbers

I need to write a class, Numbers, with methods addNumbers and currentSum, that prints the current sum. Eg.:
numbers = Numbers()
numbers.addNumber(3)
numbers.addNumber(2)
print numbers.currentSum()
should print 5
I have made a class:
class Numbers:
def __init__(self, numbers):
self.numbers=addNumbers
return numbers.currentSum()
Numbers=Numbers()
A hint, anyone?
class Numbers:
def __init__(self):
self.__sum=0
def addNumber(self, number):
self.__sum += number
def currentSum(self):
return self.__sum
Hints:
You need to implement the currentSum() method
You also need to implement the addNumber() method
addNumber() will need to keep track of numbers across method calls. Use a list.
More reading on methods: What is a "method" in Python?
class Numbers(object):
def __init__(self):
self.sum = 0
def addNumber(self, n):
self.sum += n
def currentSum(self):
return self.sum

Categories

Resources