I didn't find any info about the implementation of the factory design pattern using __init_subclass__ to register the product and the class. What do you think about?
https://peps.python.org/pep-0487/
class Shape:
product = None
shapes_classes = {}
def __init_subclass__(cls, **kwargs):
cls.shapes_classes[cls.product] = cls
#classmethod
def create_shape(cls, product, *args, **kwargs):
return cls.shapes_classes[product](*args, **kwargs)
def __str__(self):
return f'I am {self.__class__.__name__} shape'
def area(self):
raise NotImplemented
class Triangle(Shape):
product = 'triangle'
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return self.height * self.base / 2
class Square(Shape):
product = 'square'
def __init__(self, base, ):
self.base = base
def area(self):
return self.base ** 2
#Usage:
Shape.shapes_classes
{'triangle': __main__.Triangle, 'square': __main__.Square}
triangle = Shape.create_shape(product='triangle',base=3,height=4)
square=Shape.create_shape(product='square',base=3)
print(triangle)
I am Triangle shape
print(square)
I am Square shape
triangle.area()
6.0
square.area()
9
Related
I have found a code in realpython.com about python super() and I don't understand what is the purpose of the super() in Rectangle and Triangle init method if both classes have no parent (don't inherit).
class Rectangle:
def __init__(self, length, width, **kwargs):
self.length = length
self.width = width
super().__init__(**kwargs)
def area(self):
return self.length * self.width
class Square(Rectangle):
def __init__(self, length, **kwargs):
super().__init__(length=length, width=length, **kwargs)
class Triangle:
def __init__(self, base, height, **kwargs):
self.base = base
self.height = height
super().__init__(**kwargs)
def tri_area(self):
return 0.5 * self.base * self.height
class RightPyramid(Square, Triangle):
...
This way, these classes can work with multiple-inheritance, and they may get ancestors unknown at coding time - the call to super, passing any unknown parameters they may have got, ensures they will play nice when used this way.
For example, let's suppose these shapes are used to represent the creation concrete 3D printing plastic objects.
class Print3D:
def __init__(self, *, filament="PLA", **kw):
self.filament=filament
print("Print3D part initialized")
super().__init__(**kwargs)
One now can do:
class PrintedSquare(Square, Print3D):
pass
mysquare = PrintedSquare(length=20, filament="PVC")
and everything will just work.
class Polygon():
def __init__(self, side_lengths):
self.side_lengths = side_lengths
def perimeter(self):
return sum(self.side_lengths)
def __str__(self):
side_lengths = len(self.side_lengths)
return 'Polygon with {} sides'.format(side_lengths)
class Triangle(Polygon):
def __init__(self, side_lengths):
super().__init__(side_lengths)
def area(self):
s = sum(self.side_lengths)/2
a = self.side_lengths[0]
b = self.side_lengths[1]
c = self.side_lengths[2]
return float((s*(s-a)*(s-b)*(s-c)) ** 0.5)
def __str__(self):
return 'Triangle, Area: {:.2f} units^2, Perimeter: {:.2f} units'.format(self.area(), self.perimeter())
class Rectangle(Polygon):
def __init__(self, side_lengths):
super().__init__(side_lengths)
def area(self):
return float(self.side_lengths[0]*self.side_lengths[1])
def __str__(self):
return 'Rectangle, Area: {:.2f} units^2, Perimeter: {:.2f} units'.format(self.area(), self.perimeter())
class Square(Rectangle):
def __init__(self, side_length):
self.side_length = side_length
def area(self):
return float(4*side_length)
def __str__(self):
return 'Square, Area: {:.2f} units^2, Perimeter: {:.2f} units'.format(self.area(), self.perimeter())
For this program, I want to calculate the area of the square when the input of side_length is a float instead of a list, but name error occurs. How to deal with such issue?
class Square(Rectangle):
def __init__(self,side_length):
super().__init__(side_length)
I also tried this method, it does not work as well.
I am working on a game in which I have completed the engine and the networking layers. I am now working on some GUI for the game using PyQt5 (as the drawing isn't complex). I made a custom QWidget for drawing the game board which is just a widget that positions custom QPushButtons inside itself. Here's the code for that:
class BoardButton(QtWidgets.QPushButton):
tilePressed = pyqtSignal(Tile)
def __init__(self, tile: Tile = None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setAutoFillBackground(True)
self.setCheckable(False)
self.setDefault(False)
self.setFlat(False)
self.raise_()
self.tile = tile
self.default_background(tile)
self.highlight = False
self.is_clicked = False
def __change_bgd(self, colour):
p = self.palette()
p.setColor(self.backgroundRole(), colour)
self.setPalette(p)
def default_background(self, tile: Tile):
if tile == self.tile:
self.__change_bgd(QtGui.QColor(153, 76, 0))
def highlight_background(self, tile: Tile):
if tile == self.tile:
self.__change_bgd(QtCore.Qt.green)
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
self.tilePressed.emit(self.tile)
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
if self.highlight:
self.highlight_background(self.tile)
else:
self.default_background(self.tile)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
painter.setPen(QtGui.QPen(QtCore.Qt.black, QtCore.Qt.SolidLine))
painter.drawRect(0, 0, self.width(), self.height())
if self.tile:
if self.tile.is_special:
first_line = self.width() / 3
second_line = first_line * 2
painter.drawLine(first_line, 0, first_line, self.height())
painter.drawLine(second_line, 0, second_line, self.height())
painter.drawLine(0, first_line, self.width(), first_line)
painter.drawLine(0, second_line, self.width(), second_line)
if self.tile.is_exit:
painter.setBrush(QtCore.Qt.NoBrush)
painter.drawEllipse(self.width() / 4, self.height() / 4, self.width() / 2, self.height() / 2)
if self.tile.piece is not None:
if self.tile.piece.is_white:
colour = QtCore.Qt.white
else:
colour = QtCore.Qt.black
painter.setBrush(QtGui.QBrush(colour, QtCore.Qt.SolidPattern))
painter.drawEllipse(0, 0, self.width(), self.height())
if self.tile.piece.is_king:
painter.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern))
painter.drawEllipse(self.width() / 4, self.height() / 4, self.width() / 2, self.height() / 2)
class GameBoard(QtWidgets.QWidget):
tilePressed = pyqtSignal(Tile)
boardUpdate = pyqtSignal()
def __init__(self, game: Game = None, is_white: bool = None, playable=True, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setGeometry(QtCore.QRect(20, 20, 650, 650))
self.setObjectName("boardWidget")
self.game = game
self.is_white = is_white
self.playable = playable
self.buttons = []
self.__selected = None
self.tilePressed.connect(self.__tile_pressed)
def init_buttons(self):
def pos_to_name(x, y):
return f'btn_{x}{y}'
self.buttons = []
board = self.game.board
board_size = board.width
if board.width > board.height:
board_size = board.width
elif board.width < board.height:
board_size = board.height
btn_size = self.width() / board_size
start_x = self.width() / 2 - btn_size * (board.width / 2)
start_y = self.height() / 2 - btn_size * (board.height / 2)
for y in range(board.height):
row = []
for x in range(board.width):
button = BoardButton(board[y][x], parent=self)
button.setGeometry(QtCore.QRect(start_x + x * btn_size, start_y + y * btn_size, btn_size, btn_size))
button.setObjectName(pos_to_name(x, y))
button.tilePressed.connect(self.tilePressed.emit)
row.append(button)
self.buttons.append(row)
def __get_button(self, tile: Tile) -> BoardButton:
return self.buttons[tile.y][tile.x]
def update(self) -> None:
self.boardUpdate.emit()
for y, row in enumerate(self.buttons):
for x, button in enumerate(row):
button.tile = self.game.board[y][x]
button.update()
super().update()
def remove_highlights(self):
self.__selected = None
for row in self.buttons:
for button in row:
button.highlight = False
button.is_clicked = False
def highlight_buttons(self, source_tile: Tile):
self.__selected = source_tile
for tile in self.game.board.valid_moves(source_tile):
self.buttons[tile.y][tile.x].highlight = True
def move_piece(self, target_tile: Tile):
if not self.__selected:
raise ValueError('Cannot move piece because no piece is selected.')
self.game.move(self.__selected, target_tile)
def is_highlight(self, tile: Tile) -> bool:
return self.__get_button(tile).highlight
def is_clicked(self, tile: Tile) -> bool:
return self.__get_button(tile).is_clicked
def set_clicked(self, tile: Tile, value: bool):
self.__get_button(tile).is_clicked = value
def __tile_pressed(self, tile: Tile):
if not tile or self.game.game_over or not self.playable:
return
if self.is_white is not None and not is_turn(self.is_white, self.game):
return
if not tile.piece and self.is_highlight(tile):
try:
self.move_piece(tile)
except (BoardGameException, Win):
pass
self.remove_highlights()
elif not tile.piece and not self.is_highlight(tile):
self.remove_highlights()
elif tile.piece:
if self.is_clicked(tile):
self.remove_highlights()
elif self.game.is_turn(tile):
self.remove_highlights()
self.highlight_buttons(tile)
self.set_clicked(tile, True)
self.update()
This code works perfectly with one of my windows:
class LocalGameWindow(Ui_FrmLocalGame):
def __init__(self, game, playable=True):
super().__init__(game=game, playable=playable)
self.init_gameboard()
self.gameboard.update()
if self.gameboard.playable:
self.btnUndo.clicked.connect(self.btn_undo_clicked)
else:
self.setWindowTitle(...)
self.btnUndo.setText('Play')
self.lblTurn.setVisible(False)
self.lblBlackPieces.setVisible(False)
self.lblWhitePieces.setVisible(False)
self.btnExit.clicked.connect(self.close)
def btn_undo_clicked(self):
self.gameboard.game.undo()
self.gameboard.update()
Which inherits from the following (Ui_FrmLocalGame was generated with Qt Designer):
class Ui_FrmLocalGame(_GameBoardWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setupUi(self)
def setupUi(self, FrmLocalGame):
...
Which in turn inherits from:
class _GameBoardWindow(GameWidget):
def __init__(self, client: Client = None, *args, **kwargs):
super().__init__()
is_white = None
if client:
is_white = client.is_white
self.gameboard = GameBoard(is_white=is_white, *args, **kwargs, parent=self)
self.client = client
self.gameboard.boardUpdate.connect(self.update_labels)
def init_gameboard(self, game: Game = None):
if game:
self.gameboard.game = game
self.gameboard.init_buttons()
def update_labels(self):
game = self.gameboard.game
if not game:
return
if not game.game_over:
text = f"{'Black' if game.black.is_turn else 'White'}'s Turn"
else:
text = f"{'Black' if game.black.won else 'White'} Won!"
self.lblTurn.setText(text)
self.lblBlackPieces.setText(f'Black: {game.board.num_black}/{game.board.num_start_black}')
self.lblWhitePieces.setText(f'White: {game.board.num_white}/{game.board.num_start_white}')
This gives me the result I expect, it draws the gameboard widget on the window widget:
However, when I try to do seemingly the same thing with a different window widget (the online window), it just doesn't appear on the window as it does with the local game window. Here's the code for the window:
class Ui_FrmOnlineGame(_GameBoardWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setupUi(self)
def setupUi(self, FrmOnlineGame):
...
class OnlineGameWindow(Ui_FrmOnlineGame):
def __init__(self, client: Client):
super().__init__(client=client)
self.btnSend.clicked.connect(self.btn_send_clicked)
self.txtChat.returnPressed.connect(self.btn_send_clicked)
self.btnExit.clicked.connect(self.close)
self.__timer = QtCore.QTimer(self)
self.__timer.setInterval(100)
self.__timer.timeout.connect(self.__game_loop)
self.__timer.start()
def __game_loop(self):
try:
msg = self.client.recv_msg()
except BlockingIOError:
return
...
elif msg.startswith(Message.GameUpdate.value):
game = Game.from_serial(msg[1:])
self.gameboard.game = game
if not len(self.gameboard.buttons):
self.init_gameboard()
self.gameboard.update() # This should draw the gameboard widget like it does on the other window
...
def display_chat(self, text: str):
...
def display_colour(self):
show_dialog(f"You are {'white' if self.client.is_white else 'black'}.", self, 'Game Start', modal=False)
def btn_send_clicked(self):
...
def close(self) -> bool:
self.__timer.stop()
self.client.exit()
return super().close()
I've tried many things such as checking the custom widget's geometry, raising it, and making sure that it does have a parent (which it does). What have I done wrong? Any help would be appreciated, and sorry about all the code, I just feel it's necessary to find the issue.
I want to create a class(say, LockedAttributes) to access(READ/WRITE) some attributes safely by multiple threads.I want to pass those attributes that I want to share as a list to the LockedAttributes class. Some of the list elements are itself class objects with it's own setter and getter.
How can i access those setter/getter from a LockedAttribute class obj?
My use of getattr() setattr() might be wrong.
sample code:
class Coord:
def __init__(self, x=0.0, y=0.0, z=0.0):
self.x = x
self.y = y
self.z = z
def set_coordinator(self, x, y, z):
self.x = x
self.y = y
self.z = z
def get_coordinator(self):
return self.x, self.y, self.z
class LockedAttributes(object):
def __init__(self, obj):
self.__obj = obj
self.__lock = RLock()
def getmyPosition(self):
with self.__lock:
return self.__obj[0]
def getmySpeed(self):
with self.__lock:
return self.__obj[1]
def getcolPosition(self):
with self.__lock:
return self.__obj[2]
def getDistfromCol(self):
with self.__lock:
getattr(self, self.__obj[3])
def setDistfromCol(self, value):
with self.__lock:
setattr(self, self.__obj[3], value)
def getcolactivationFlag(self):
with self.__lock:
getattr(self, self.__obj[4])
def setcolactivationFlag(self, value):
with self.__lock:
setattr(self, self.__obj[3], value)
class OBU():
def __init__(self):
pos = Coord()
speed = Coord()
colpos = Coord()
distance_from_accident = 0.0
Flag = False
self.shared_attributes = LockedAttributes([ pos, speed, colpos, distance_from_accident, Flag])
mypos= self.shared_attributes.getmyPosition()
mypos.get_coordinator() # Not workinh
The __init__ method of the LockedAttributes class should take an argument so that you can actually pass a list object to it.
Change:
class LockedAttributes(object):
def __init__(self):
self.__obj = object
self.__lock = RLock()
To:
class LockedAttributes(object):
def __init__(self, obj):
self.__obj = obj
self.__lock = RLock()
What is the simplest / most pythonic way to override only the setter of an abstract property in Python 3? Variant 3 seems to mean the least effort for the derived class implementor. Is it correct? Does it have disadvantages?
import abc
class A1(metaclass=abc.ABCMeta):
def __init__(self, x, **kwargs):
super().__init__(**kwargs)
self._x = x
#property
def x(self):
return self._x
#x.setter
#abc.abstractmethod
def x(self, value):
self._x = value
class B1(A1):
#property
def x(self):
return super().x
#x.setter
def x(self, value):
print("B1 setter")
super(B1, self.__class__).x.fset(self, value)
b1 = B1(x=1)
b1.x = 3
print(b1.x)
class A2(metaclass=abc.ABCMeta):
def __init__(self, x, **kwargs):
super().__init__(**kwargs)
self._x = x
#abc.abstractmethod
def _get_x(self):
return self._x
#abc.abstractmethod
def _set_x(self, value):
self._x = value
x = property(_get_x, _set_x)
class B2(A2):
def _get_x(self):
return super()._get_x()
def _set_x(self, value):
print("B2 setter")
super()._set_x(value)
x = property(_get_x, _set_x)
b2 = B2(x=1)
b2.x = 3
print(b2.x)
class A3(metaclass=abc.ABCMeta):
def __init__(self, x, **kwargs):
super().__init__(**kwargs)
self._x = x
def _get_x(self):
return self._x
#abc.abstractmethod
def _set_x(self, value):
self._x = value
x = property(
lambda self: self._get_x(),
lambda self, value: self._set_x(value))
class B3(A3):
def _set_x(self, value):
print("B3 setter")
super()._set_x(value)
b3 = B3(x=1)
b3.x = 3
print(b3.x)
So, yes, you listed a lot of ways in there - and although the one that requires more code is your variant 3, the most straighforard, least surprising way to do it is your variant 1 -
It just works, and is perfectly readable, no surprises - and there seems to be no simpler way than calling fget explicitly there.