Python Forum
ABC Module and @property decorator, Pythonic Way?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
ABC Module and @property decorator, Pythonic Way?
#11
(Aug-17-2021, 07:50 PM)muzikman Wrote: Is it for name mangling? So, that derived classes can't mess with them because
Yes so can keep copy of original variable/method,so subclass can nor overwrite it.
Simple example.
class H:
    def __init__(self):
        self.var = 3

class W(H):
    def __init__(self):
        super().__init__()
        self.var = 100
>>> obj = W()
>>> obj.var
100
So in this case is 3 is lost and only keep 100 when use the sub class.
class H:
    def __init__(self):
        self.__var = 3 # Keep copy of original variable

class W(H):
    def __init__(self):
        super().__init__()
        self.__var = 100
>>> obj = W()
>>> obj._W__var
100
>>> obj._H__var
3
Quote:will that show me the new values of the dunder methods and properties?
Yes.

Quote:can you explain name mangling for me and the purpose?
If watch video at this time he explain it well.
From Doc.
Reply
#12
From PEP8
Quote:If your class is intended to be subclassed, and you have attributes that you do not want subclasses to use, consider naming them with double leading underscores and no trailing underscores. This invokes Python's name mangling algorithm, where the name of the class is mangled into the attribute name. This helps avoid attribute name collisions should subclasses inadvertently contain attributes with the same name.

Note 1: Note that only the simple class name is used in the mangled name, so if a subclass chooses both the same class name and attribute name, you can still get name collisions.

Note 2: Name mangling can make certain uses, such as debugging and __getattr__(), less convenient. However the name mangling algorithm is well documented and easy to perform manually.

Note 3: Not everyone likes name mangling. Try to balance the need to avoid accidental name clashes with potential use by advanced callers.
Examples are more difficult to come by. I ran across this supposed problem and solution. This is the problem. Parent.__init__ should always call Parent.test(), but it calls Child.test() when a Child object is created:
class Parent:
  def __init__(self):
    print('in init')
    self.test()
  def test(self):
    print('In Parent test method')

class Child(Parent):
  def test(self):
    print('In Child test method')

obj = Child()
obj.test()
Output:
in init In Child test method In Child test method
The solution is to make use a "private" attribute.
class Parent:
  def __init__(self):
    print('in init')
    self.__test()
  def test(self):
    print('In Parent test method')

  # private copy
  __test = test

class Child(Parent):
  def test(self):
    print('In Child test method')

obj = Child()
obj.test()
Output:
in init In Parent test method In Child test method
A problem with this example is there is already a better solution.
class Parent:
  def __init__(self):
    print('in init')
    Parent.test(self)  # I want to call test() defined in the Parent class

  def test(self):
    print('In Parent test method')

class Child(Parent):
  def test(self):
    print('In Child test method')

obj = Child()
obj.test()
Output:
in init In Parent test method In Child test method
But I think this futility demonstrates how rarely something like this is needed, or even wanted. Usually you want subclasses to inherit your attributes.

I like this article that talks about name mangling and a few other thing's you've asked about. I came across it while searching for a good name mangling example. Still haven't found one.

https://izziswift.com/python-name-mangling/
Reply
#13
I don't see many classes with private variables. I believe what you said, that develop classes so everything can be inherited and overriden. This is what I try to do.

If a method is static or a classmethod or class property, I can understand why using the class name to access it is used. However, inside the class I prefer to use
self.__class__.property
.

To access class methods and properties, I see a lot of
ClassName.pi
to reference them. I understand because there would be no other way to reference the class variable or method because they're constant through instantiations.

Aside from what I just mentioned, when would I need to prepend the ClassName and not the instance?

I will read that article and go over the video again.

Thanks again
Reply
#14
Sometimes you really do want to just call the method that is defined by the class and not any method that might be defined by a subclass. this.__class__ does not work in this case because this.__class__ returns the class of the object, not the class that defined the method.
class A():
    def __init__(self):
        super().__init__()
        print('A', self.__class__)

class B(A):
    def __init__(self):
        super().__init__()
        print('B', self.__class__)

class C(B):
    def __init__(self):
        super().__init__()
        print('C', self.__class__)

c = C()
Output:
A <class '__main__.C'> B <class '__main__.C'> C <class '__main__.C'>
I actually have a real world example for why calling the class local method is sometimes needed.

I have a TextEditor class that I can used to open, edit and save text files. I have a Workspace class that is like a TextEditor, but it is for editing code. It adds syntax highlighting and code execution. I also have a Brower class that is like a Workspace class. In addition to editing and executing code, it understands classes and inheritance and adds a view for navigating classes and methods.

Each of these classes has a layout method that organizes all the parts inside a view. I call the layout method inside the constructor for the class and this introduces a problem. Workspace defines attributes that are not part of TextEditor. If I create a Workspace, TextEditor.__init__ will call Worspace.layout instead of TextEditor.layout. This will crash when it tries to layout parts that haven't been created yet.
class A():
    def __init__(self):
        super().__init__()
        self.layout()

    def layout(self):
        print('A layout')

class B(A):
    def __init__(self):
        super().__init__()
        self.layout()

    def layout(self):
        print('B layout')

class C(B):
    def __init__(self):
        super().__init__()
        self.layout()

    def layout(self):
        print('C layout')

c = C()
Output:
C layout C layout C layout
What I really want to do is call the layout method defined in TextEditor. So instead of calling self.layout() I call TextEditor.layout(self) inside TextEditor.__init__()
class A():
    def __init__(self):
        super().__init__()
        A.layout(self)

    def layout(self):
        print('A layout')

class B(A):
    def __init__(self):
        super().__init__()
        B.layout(self)

    def layout(self):
        print('B layout')

class C(B):
    def __init__(self):
        super().__init__()
        C.layout(self)

    def layout(self):
        print('C layout')

c = C()
Output:
A layout B layout C layout
Now you might say "Why not call layout after you create the new instance?
workspace = Workspace()
workspace.layout()
That solves the problem, but it is ugly. If I want a Workspace or a Browser I want it to appear fully formed without having to do any configuration.

The dunder method could also be used. I could define a class attribute that references the local method
class A():
    def __init__(self):
        super().__init__()
        self.__layout()

    def layout(self):
        print('A layout')

    __layout = layout

class B(A):
    def __init__(self):
        super().__init__()
        self.__layout()

    def layout(self):
        print('B layout')

    __layout = layout

class C(B):
    def __init__(self):
        super().__init__()
        self.__layout()

    def layout(self):
        print('C layout')

    __layout = layout

c = C()
Output:
A layout B layout C layout
I think the "class.method(self)" approach is clearer than "__method = method".
Reply
#15
I get what you're saying and yes, "I think the "class.method(self)" approach is clearer than "__method = method"." I agree with that totally.

I don't really like the idea of __test = test. It's a little sloppy.

I am reading a book on Python and the project is creating a Black Jack game. There is no inheritance used at all. However, there are instances of other classes being created in a class. Inheritance would not work in this situation at all.

I have created 4 very lean classes with at most 3 methods and the logic is defined outside of the classes. It seems to be coming along but it's not easy. At first, I thought that I would need a player class, then I thought to myself, "The dealer is a player as well." A player class is not what I am looking for but I would need a class that would keep track of the cards in each person's hand. The Hand class is going to define the players. Each instance is a player. So, I have the following classes:

Some level 0 tuples and dictionary to define the cards.

suits = ('Heart', 'Clubs',.....)
rank = ('Two', 'Three', 'Four'........)
values = {'Two' : 2, 'Three' : 3, ..............)  # This is how we get the integer value of the card

class Card:
    pass

class Deck:
    
    # Create a deck with a for loop for suit nested for loop for rank. Now we can get the value in the dictionary by passing the rank.
     # Create instance of card class inside the nested loop assigning each card to an empty list.
      current_card = Card(suit, rank)
     def shuffle(self):
        random.shuffle(card list):
         

class Hand:
   pass # an instance of this class creates a player who has a hand. Keeps track of dealer and players hand and totals. 

Class Chips
  pass
My point is, not everything needs to be inherited. At first approach, I was convinced of the need for a player class but what I needed was something even more abstract, the Hand class. So, I decided to take the approach above and it started working. I am using a Jupiter notebook so I can execute the parts of code that I want without continuing to re-run the entire program.

This is what I have thus far. I don't want hints or anything on this. I need to do this on my own. Please forgive the formatting. I just pasted it in really quick.

class Card:

    def __init__(self, suit, rank):
        self.suit = suit
        self.rank = rank
        self.value = values[rank]

    def __str__(self):
        return f'{self.rank} of {self.suit}'

class Deck:

    def __init__(self):

        self.deck = []  # start with an empty list
       
        for suit in suits:
            for rank in ranks:
                create_card = Card(suit, rank)
                self.deck.append(create_card)

    def __str__(self):
        
        deck_comp = ''  # start with an empty string
        for card in self.deck:
            deck_comp += '\n '+card.__str__() # add each Card object's print string
        return 'The deck has:' + deck_comp

    def shuffle(self):
        random.shuffle(self.deck)

    def deal(self):
        single_card = self.deck.pop()
        return single_card
      
class Hand:
    def __init__(self):
        self.cards = []  # start with an empty list as we did in the Deck class
        self.value = 0   # start with zero value
        self.aces = 0    # add an attribute to keep track of aces
    
    def add_card(self,card):
        self.cards.append(card)
        self.value += values[card.rank]
   
    def adjust_for_ace(self):
        if self.value == 11:
            self.aces += 1     
 
class Chips:
    
    def __init__(self):
        self.total = 100  # This can be set to a default value or supplied by a user input
        self.bet = 0
        
    def win_bet(self):
        self.total += self.total
    
    def lose_bet(self):
        self.total -= self.total

new_deck = Deck()
new_deck.shuffle()
dealer = Hand()
player = Hand()
Reply
#16
You might want to use inheritance and make Hand and Deck subclasses of list so you automatically get all the list functionality without having to write code. If you gave Card a __repr__ you wouldn't need __str__ for Card or Hand.

All software has places where it makes sense to use "is a" (inheritance) and "has a" (composition).
Reply
#17
They should be subclasses of which class? The list class?

What is __repr__? Regex?

(Aug-18-2021, 04:12 PM)deanhystad Wrote: You might want to use inheritance and make Hand and Deck subclasses of list so you automatically get all the list functionality without having to write code. If you gave Card a __repr__ you wouldn't need __str__ for Card or Hand.

All software has places where it makes sense to use "is a" (inheritance) and "has a" (composition).

Can you elaborate on this last line a little?

Thanks
Reply
#18
repr is like str, but it may produce a less polished result with more information.

Yes, list. Hand and Deck look a lot like lists. Why not make them lists with extra features? Change the Hand/list relationship from "has a" to "is a".
Reply
#19
(Aug-18-2021, 05:09 PM)deanhystad Wrote: repr is like str, but it may produce a less polished result with more information.

I will research it.

Yes, list. Hand and Deck look a lot like lists. Why not make them lists with extra features? Change the Hand/list relationship from "has a" to "is a".

oh ok. Gotcha.


Does your career involve Python development?
Reply
#20
Software development. Mostly machine controls. Python is new to me, but it is in demand and I need to adapt.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  the order of running code in a decorator function akbarza 2 1,284 Nov-10-2023, 08:09 AM
Last Post: akbarza
  Pythonic from a C++ perspective PyDan 2 1,377 Sep-18-2023, 11:39 AM
Last Post: PyDan
  Curious about decorator syntax rjdegraff42 14 4,715 May-03-2023, 01:21 PM
Last Post: rjdegraff42
  Subclass initialized property used in parent class method. Is it bad coding practice? saavedra29 5 3,499 Feb-07-2022, 07:29 PM
Last Post: saavedra29
  @property vs __set__ / __get__ and __setattr__ / __getattr__ okhajut 1 5,174 Jun-15-2021, 03:48 PM
Last Post: snippsat
  Can property getters and setters have additional arguments? pjfarley3 2 4,167 Oct-30-2020, 12:17 AM
Last Post: pjfarley3
  decorator adamfairhall 0 1,921 Aug-18-2020, 08:38 AM
Last Post: adamfairhall
  Property price calculation oli_action 4 4,438 Jul-15-2020, 04:27 PM
Last Post: sridhar
  Use of @property decorator ruy 16 9,917 Jun-09-2020, 05:29 PM
Last Post: buran
  Is this use of exec pythonic? psolar 1 2,343 Feb-07-2020, 12:23 PM
Last Post: buran

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020