在Python中重写嵌套类

2024-10-01 22:28:18 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个基类,里面有这样的类

import pandas as pd

book_df = pd.DataFrame(
    {
        'title': [f'title{num}' for num in range(5)],
        'due date': pd.date_range(start='2021-07-26', periods=5, freq='D').to_list(),
        'status': ['delay', 'delay', 'return', 'good', 'good']
    })

class BaseBookInfo(object):
    def __init__(self, df):
        self.counter = self.Counter(df)
    class Counter(object):
        def __init__(self, df):
            self.df = df
            self.total = len(self.df)
            self.status = self.count_status()
        def count_status(self):
            return self.df['status'].value_counts()

。。我创建了一个具有如下重写的子类

class BooksBorrowed(BaseBookInfo):
    status_type = {
        'ZERO_BORROW': 'no borrowed books',
        'DELAY_EXIST': 'there are delayed books!!',
        'NO_PROBLEM': 'there is no problem!!',
    }
    def __init_(self, df):
        super().__init__(df)
        self.counter = self.Counter(df, self.status_type)
    class Counter(BaseBookInfo.Counter):
        def __init__(self, df, st_type):
            super().__init__(df)
            self.types = st_type
        def show_status(self):
            is_delayed = self.status['good'] != self.total
            if self.total:
                if is_delayed:
                    result = 'DELAY_EXIST'
                else:
                    result = 'NO_PROBLEM'
            else:
                result = 'ZERO_BORROW'
            print(f'{self.types[result]}')

当我测试BooksBorrowed时,我得到了这个错误

Traceback (most recent call last):
  ....
  File "D:/repository/BillMaker/src/billmaker/scraper/test_stackoverflow.py", line 54, in <module>
    bb = BooksBorrowed(book_df)
  File "D:/repository/BillMaker/src/billmaker/scraper/test_stackoverflow.py", line 15, in __init__
    self.counter = self.Counter(df)
TypeError: __init__() missing 1 required positional argument: 'st_type'

我希望来自子类的Counter类的super().__init__()调用将连接到基类的内部类,但我错了

你能告诉我在我的情况下什么是错误的想法吗

我不想更改BooksBorrowed.status_type的位置,因为它也将在BookBorrowed中使用

更新: 在问了这个问题之后,我开始阅读关于这个主题的文章。我发现我的示例与破坏Liskov substitution principle的典型案例非常相似。此外,我开始知道什么是has a关系,@simonCrowe已经在下面进行了评论。所以我现在明白了,这是一个更好的组合案例,而不是继承案例。谢谢你的评论


Tags: inselfdfinitdeftypestatuscounter
2条回答

我引用了this book,发现我的代码违反了许多继承规则。 下面引用的所有区块部分都来自本书

  • 我打破了继承的规则

    • 可替代性:

      Using a different instance creation signature is a common way to violate substitutability.

      • 在我的代码中,BooksBorrowed.Counter正在破坏substitutabiltiy
    • 超类访问:

      Overriding methods to take different numbers of arguments, and passing only some of them along using super(), can lead to confusion and poor maintainability.

      • 在我的代码中,BooksBorrowed.Counter.__init__使用不同数量的参数
    • has-a关系

      Use composition for has-a relationships

      • 在我的代码中,“计数器”has a“状态类型”。但是,status-type是以继承方式重写的
  • 如何使用作文方式

    Inversion of control says that instaed of creating instances of dependencies in your class, you can pass in existing instances for the clas to make use of.

    • 在我的代码中有两种关系。它们是“BaseBookInfo”-“计数器”和“计数器”-“状态类型” 它们彼此紧密耦合,因此很难升级。它们需要松散耦合

这是我最后的代码,我觉得更好。如果您发现任何部分可以进一步改进,请回答我的问题,我将接受您的问题。我不想接受我的答案

class BaseCounter(object):
    def __init__(self, df):
        self.df = df
        self.total = len(self.df)
        self.status = self.count_status()
    def count_status(self):
        return self.df['status'].value_counts()

class BaseBookInfo(object):
    def __init__(self, df, counter):
        self.df = df
        self.counter = counter

bi = BaseBookInfo(book_df, BaseCounter(book_df))

class BBCounter(BaseCounter):
    def __init__(self, df):
        super().__init__(df)
        self.types = dict()
    def get_status_type(self, types):
        self.types = types
    def show_status(self):
        is_delayed = self.status['good'] != self.total
        if self.total:
            if is_delayed:
                result = 'DELAY_EXIST'
            else:
                result = 'NO_PROBLEM'
        else:
            result = 'ZERO_BORROW'
        print(f'{self.types[result]}')

class BooksBorrowed(BaseBookInfo):
    status_type = {
        'ZERO_BORROW': 'no borrowed books',
        'DELAY_EXIST': 'there are delayed books!!',
        'NO_PROBLEM': 'there is no problem!!'
    }
    def __init__(self, df, counter):
        super().__init__(df, counter)
        self.counter.get_status_type(self.status_type)

bb = BooksBorrowed(book_df, BBCounter(book_df))

(下面,我保留了嵌套的类结构。但是请注意,只要将Counterclass属性指定给所需的类,就可以在book类之外使用您喜欢的任何名称轻松地定义类本身。)


将如何实例化Counter类的知识从__init__中分离出来,并将其放入类方法中

class BaseBookInfo:
    class Counter:
        def __init__(self, *, df, **kwargs):
            super().__init__(**kwargs)
            self.df = df
            self.total = len(self.df)
            self.status = self.count_status()
        def count_status(self):
            return self.df['status'].value_counts()

    def __init__(self, *, counter, **kwargs):
        super.__init__(**kwargs)
        self.counter = counter

    @classmethod
    def from_dataframe(cls, df, **kwargs):
        c = cls.Counter(df=df, **kwargs)
        return cls(counter=c)

现在,调用方负责确保Counter被正确实例化,或者手动实例化

b = BaseBookInfo(counter=BaseBookInfo.Counter(df))

或者通过专用的构造函数

b = BaseBookInfo.from_dataframe(df=df)

当您将BaseBookInfo子类化时,不需要为__init__from_dataframe提供新的定义;它们已经足够通用,可以处理您定义的任何类属性Counter

class BorrowedBook(BaseBookInfo):
    class Counter(BaseBookInfo.Counter):

        # These are Counter attributes, not BorrowedBook attributes
        ZERO_BORROW = 'no borrowed books'
        DELAY_EXIST = 'there are delayed books!!'
        NO_PROBLEM = 'there is no problem!!'
        
        def __init__(self, *, st_type, **kwargs):
            super().__init__(**kwargs)
            self.types = st_type

        def show_status(self):
            is_delayed = self.status['good'] != self.total
            if not self.total:
                result = self.ZERO_BORROW
            elif is_delayed:
                result = self.DELAY_EXIST
            else:
                result = self.NO_PROBLEM
            print(result)

bb1 = BorrowedBook(counter=BorrowedBook.Counter(df=df, st_type=stat))
bb2 = BorrowedBook.from_dataframe(df, st_type=stat)

bb2的情况下,BorrowedBook是继承的from_dataframe类方法的cls参数,因此将实例化正确的Counter

相关问题 更多 >

    热门问题