zoukankan      html  css  js  c++  java
  • 抽象数据类型和python类

    阅读目录

    • 一、抽象数据类型
    • 二、python的类
    • 三、类的定义和使用
    • 四、python异常
    • 五、类定义实例:学校认识管理系统中的类 
    • 六、部分课后编程练习

    一、抽象数据类型

    抽象数据类型(ADT)是计算机领域的一种思想和方法,是一种用于设计和实现程序模块的有效技术。模块通过接口来提供功能所需的信息,并不涉及具体实现细节。

    1、数据类型和数据构造
    python提供很多的数据类型,但是无论提供多少内置类型,在处理复杂问题的时候,程序员都需要根据自己需求把一组数据组织起来,构成一个数据对象,作为一个整体存储、传递和处理。

    例子:有理数的表示,当然可以使用两个变量表示一个有理数。

    a1 = 3
    b1 = 5
    
    # 两个有理数之和
    def rational_plus(a1, b1, a2, b2):
        num = a1*b2+b1*a2
        den = b1*b2
        return num,den
    a2, b2 = rational_plus(a1, b1, 7, 10)
    用变量表示有理数

    但是这样会有严重的管理问题:

    • 编程者需要时刻记住哪两个变量记录的是分子和分母。
    • 如果换一个有理数运算,需要频繁更换变量名 。
    • 非常容易出现错误。
    r1 = (3, 5)
    r2 = (7,10)
    def rational_plus(r1, r2):
        num = r1[0]*r2[1]+r2[0]*r1[1]
        den = r1[1]*r1[1]
        return num,den
    改进版本

    情况好了一点,这就是数据构造和组织的作用。但是依然存在问题:

    • 不能将元组跟有理数区分,如果程序中再出现一种表示其他数据的二元组,那么无法区分出来。
    • 有理数的相关操作跟有理数没有绑定关系。
    • 有理数运算需要直接按照位置获得元素,如果对于复杂数据对象,要记住每个元素的位置是非常繁琐的。

    2、抽象数据类型的概念

    其实上面的有理数例子暴露出两个本质问题

    • 数据表示的完全暴露。
    • 对象使用和操作对具体表示的依赖性。

    要解决这两个问题就需要ADT(Abstract Data Type)思想。

    抽象数据类型提供的操作包括三类:

    •  构造操作
    •  解析操作
    •  变动操作

    python的所有内置类型都是一种抽象数据类型。比如str,int等。只不过它提供了渐变的文字量书写方式,可以看做是简化的构造操作。

    可变对象和不变对象是区别就是是否具有变动操作。即该类对象在创建之后是否允许变化。

    python中的不变对象str,tuple,frozenset是不变数据类型,而list,set和dict是可变数据类型。

    3、抽象数据类型的描述

    采用上图的描述方法来进行描述。

    ADT是一种思想,也是一种技术。

    •  围绕一类数据定义程序模块
    •  模块的接口的实现分离
    •  在需要实现的时候,从所用的编程语言中选择一套合适的机制,采用合适的技术,实现这种功能,包括数据的表示和操作。

    二、python的类

    1、有理数类

    class Rational:
        def __init__(self, num, den=1):
            self.num =num
            self.den = den
        def plus(self, another):
            den = self.den * another.den
            num = self.num * another.den + self.den * another.num
            return Rational(den, num)
    ADT版本的有理数

    2、类定义进阶

    关于python的私有属性、还有双下划线。实例方法、静态方法、类方法。还有魔术方法__add__,__mul__,__floordiv__,__eq__,__lt__,__gt__等的应用。

    class Rational:
        @staticmethod
        def _gcd(m, n):
            if n == 0:
                m, n = n, m
            while m != 0:
                m, n = n % m, m
            return n
    
        def __init__(self, num, den=1):
            if not isinstance(num, int) or not isinstance(den, int):
                raise TypeError
            if den == 0:
                raise ZeroDivisionError
            sign = 1
            if num < 0:
                num, sign = -num, -sign
            if den < 0:
                den, sign = -den, -sign
            g = Rational._gcd(num, den)
            # call function gcd defined in this class.
            self._num = sign * (num//g)
            self._den = den//g
    
        def num(self):
            return self._num
    
        def den(self):
            return self._den
    
        def __add__(self, another):     # mimic + operator
            den = self._den * another.den()
            num = (self._num * another.den() +
                   self._den * another.num())
            return Rational(num, den)
    
        def __mul__(self, another):      # mimic * operator
            return Rational(self._num * another.num(),
                            self._den * another.den())
    
        def __floordiv__(self, another):  # mimic // operator
            if another.num() == 0:
                raise ZeroDivisionError
            return Rational(self._num * another.den(),
                            self._den * another.num())
    
        # ... ...
        # Other operators can be defined similarly:
        # -:__sub__, /:__truediv__, %:__mod__, etc.
    
        def __eq__(self, another):
            return self._num * another.den() == self._den * another.num()
    
        def __lt__(self, another):
            return self._num * another.den() < self._den * another.num()
    
        # Other comparison operators can be defined similarly:
        # !=:__ne__, <=:__le__, >:__gt__, >=:__ge__
    
        def __str__(self):
            return str(self._num) + "/" + str(self._den)
    
        def print(self):
            print(self._num, "/", self._den)
    ADT进阶版本

    三、类的定义和使用

    1、类的基本定义和使用

    类定义

    class用来定义类。一个类定义确定了一个名称空间,不会与类外面的名字冲突。注意是在执行一个类定义的时候创建这个名称空间。这个名称空间将一直存在,除非明确删除del。

    类里定义的变量和函数称为这个类的属性。

    类定义可以写在程序的任何地方,只是如果放在一个函数内部,或者另外一个类的内部。那么有两点不同:

    •  建立一个局部的名称空间。
    •  不能使用import导入。

    类对象及其使用

    执行一个类定义创建一个类对象,这种对象主要支持两种操作。

    • 属性访问。
    • 实例化。

    2、实例对象:初始化和使用

    初始化操作

    twothirds = Ration(2,3)初始化的步骤:

    1. 建立一个对象
    2. 调用__init__函数给这个对象赋值
    3. 返回新建的对象
    4. 把这个对象赋值给变量

    实例的数据类型

    实例对象的数据访问,用.的方式。

    实例对象是一个独立的数据体。可以赋值给变量,传给函数,作为返回值,或者其他实例对象的属性值等。

    方法的定义和使用

    除了数据属性之外,类实例的另一种属性就是方法。例如:p是类C的实例,m是p的实例方法。方法调用p.m()相当于C.m(p)

    但是方法对象和函数对象是不同的,方法对象包含了两部分:

    •  由类中的函数定义生成的函数对象。
    •  调用时约束的一个实例对象。在这个方法对象最终执行时,其中的实例对象将被作为函数的第一个实参。也就是self。

    3、几点说明

    • 创建类对象之后,可以通过属性赋值的方式给这个类或者对象添加新属性。但是注意同名覆盖的问题。
    • 实例方法的内部调用其他实例方法用self.方法
    • 方法内部也可以通过global和nonlocal访问全局和局部空间的变量和函数。
    • python的自省机制isinstance。

    静态方法和类方法

    @staticmenthod静态方法
    @classmethod类方法

    类定义的作用域规则

    在类外采用类名.属性名的方式引用。

    在python中类的作用域的局部名字和函数作用域的局部名字不同:

    •  在函数中,局部名字的作用域自动延伸到内部嵌套的作用域。比如函数g内部函数f,那么在f里可以直接使用g中定义的变量。
    •  而在类中,局部定义的名字并不会自动延伸到嵌套的作用域中。如果需要调用,必须采用类名.属性名的方式调用。

    私有变量

    单下划线和双下划线。

    4、继承

    对于子类的方法查找顺序经典类是深度优先,新式类是广度优先。

    使用__bases__查看所有父类。

    issubclass检测两个类是否具有继承关系。

    关于静态约束和动态约束。

    class B:
        def f(self):
            self.g()
        def g(self):
            print('B.g called')
    class C(B):
        def g(self):
            print('C.g called')    
    x = C()
    x.f() #打印C.g called
    python采用动态约束

    标准函数super()的两种用法:

    •  super().m(...)。不带参数调用父类的方法,这种方法只能出现在方法函数的定义里。
    •  super(子类名,子类对象).m(....)。这个可以出现在程序的任何地方,并不要求一定出现在类的方法函数里。

    四、python异常

    1、异常类和自定义异常

    python通过关键字raise或者解释器进入异常处理模式。

    用try:exception:finally来捕获异常,进入异常处理模式。所有的异常都是继承BaseException。如果用户要自定义异常,选择一个合适的异常类来继承。

    class RationalError(ValueError):
        pass

    2、异常的传播和捕捉

    假设函数f执行过程中发生异常e。处理流程如下:

    •  解释器立即转入异常处理模式,设法找到处理e的处理器。
    •  先在发生异常的函数体(也就是f)里寻找处理器。如果发生异常的语句在try语句体里,那么顺序检查exception子句,看是否存在能处理e的处理器。
    •  如果发生异常try语句的所有处理器都不能处理。那么查看包围该try的try语句(如果存在)是否有与之匹配的处理器。
    •  如果e不能再f函数里处理,f的执行异常终止。
    •  如果上面的查找国展找到了与e相匹配的处理器,就执行该exception的代码,执行完之后,回到正常执行模式,并从try语句之后开始继续执行。
    •  如果最后也没有找到与之匹配的异常处理器,在交互式模式下,输出错误,等待下一次命令。在自主执行下,该程序立即终止。

    3、内置的标准异常类

    五、类定义实例:学校认识管理系统中的类 

    import datetime
    
    
    class PersonTypeError(TypeError):
        pass
    
    
    class PersonValueError(ValueError):
        pass
    定义两个异常类
    class Person:
        _num = 0
        
        def __init__(self, name, sex, birthday, ident):
            if not (isinstance(name, str) and
                    sex in ("", "")):
                raise PersonValueError(name, sex)
            try:
                birth = datetime.date(*birthday)  # 生成一个日期对象
            except:
                raise PersonValueError("Wrong date:", birthday)
            self._name = name
            self._sex = sex
            self._birthday = birth
            self._id = ident
            Person._num += 1  # 实例计数
    
        def id(self): return self._id
        def name(self): return self._name
        def sex(self): return self._sex
        def birthday(self): return self._birthday
        def age(self): return (datetime.date.today().year -
                               self._birthday.year)
    
        def set_name(self, name):  # 修改名字
            if not isinstance(name, str):
                raise PersonValueError("set_name", name)
            self._name = name
    
        def __lt__(self, another):
            if not isinstance(another, Person):
                raise PersonTypeError(another)
            return self._id < another._id
    
        @classmethod
        def num(cls): return cls._num
        
        def __str__(self):
            return " ".join((self._id, self._name,
                             self._sex, str(self._birthday)))
    
        def details(self):
            return ", ".join(("学号: " + self._id,
                              "姓名: " + self._name,
                              "性别: " + self._sex,
                              "出生日期: " + str(self._birthday)))
    定义一个通用的人员类
    class Student(Person):
        _id_num = 0
    
        @classmethod
        def _id_gen(cls):  # 实现学号生成规则
            cls._id_num += 1
            year = datetime.date.today().year
            return "1{:04}{:05}".format(year, cls._id_num)
        
        def __init__(self, name, sex, birthday, department):
            Person.__init__(self, name, sex, birthday,
                            Student._id_gen())
            self._department = department
            self._enroll_date = datetime.date.today()
            self._courses = {}  # 一个空字典
    
        def department(self): return self._department
        def en_year(self): return self._enroll_date.year
    
        def set_course(self, course_name):
            self._courses[course_name] = None
    
        def set_score(self, course_name, score):
            if course_name not in self._courses:
                raise PersonValueError("No this course selected:",
                                       course_name)
            self._courses[course_name] = score
    
        def scores(self): return [(cname, self._courses[cname])
                                  for cname in self._courses]
    
        def details(self):
            return ", ".join((Person.details(self),
                              "入学日期: " + str(self._enroll_date),
                              "院系: " + self._department,
                              "课程记录: " + str(self.scores())))
    
        # 还可以考虑其他有用的方法
    定义一个学生类
    class Staff(Person):
        _id_num = 0
        
        @classmethod
        def _id_gen(cls, birthday):  # 实现职工号生成规则
            cls._id_num += 1
            birth_year = datetime.date(*birthday).year
            return "0{:04}{:05}".format(birth_year, cls._id_num)
        
        def __init__(self, name, sex, birthday, entry_date=None):
            super().__init__(name, sex, birthday,
                             Staff._id_gen(birthday))
            if entry_date:
                try:
                    self._entry_date = datetime.date(*entry_date)
                except:
                    raise PersonValueError("Wrong date:", entry_date)
            else:
                self._entry_date = datetime.date.today()
            self._salary = 1720    # 默认设为最低工资, 可修改
            self._department = "未定"  # 需要另行设定
            self._position = "未定"    # 需要另行设定
    
        def set_salary(self, amount):
            if not type(amount) is int:
                raise TypeError
            self._salary = amount
    
        def set_position(self, position):
            self._position = position
        def set_department(self, department):
            self._department = department
    
        def details(self):
            return ", ".join((super().details(),
                              "入职日期: " + str(self._entry_date),
                              "院系: " + self._department,
                              "职位: " + self._position,
                              "工资: " + str(self._salary)))
                              
        # 还应包括查看salary,设置和查看院系,设置和查看职位的操作等,从略
    定义一个员工类

    六、部分课后编程练习

    class Time:
        @staticmethod
        def s_to_time(seconds):
            return (seconds//3600) % 24, (seconds%3600) // 60, (seconds % 3600) % 60
    
        def __init__(self, hours, minutes=0, seconds=0):
            if not (isinstance(hours, int) or isinstance(minutes, int) or isinstance(seconds, int)):
                raise TypeError
            if hours>24 or minutes >60 or seconds >60:
                raise ValueError
            self._hours = hours
            self._minutes = minutes
            self._seconds = seconds
    
        def hours(self):
            return self._hours
    
        def minutes(self):
            return self._minutes
    
        def seconds(self):
            return self._seconds
    
        def __add__(self, other):
            if not isinstance(other, Time):
                raise TypeError
            total_s = (self._hours + other.hours()) * 3600 + (self._minutes + other.minutes()) * 60 + self._seconds + other.seconds()
            h, m, s = Time.s_to_time(total_s)
            return Time(h, m, s)
    
        def __sub__(self, other):
            if not isinstance(other, Time):
                raise TypeError
            if self < other:
                raise ValueError
            total_s = self._hours * 3600 + self._minutes * 60 + self._seconds - other.hours() * 3600 - other.minutes() * 60 - other.seconds()
            h, m, s = Time.s_to_time(total_s)
            return Time(h, m, s)
    
        def __eq__(self, other):
            if not isinstance(other, Time):
                raise TypeError
            return self._hours == other.hours() and self._minutes == other.minutes() and self._seconds == other.seconds()
    
        def __lt__(self, other):
            if not isinstance(other, Time):
                raise TypeError
            return self._hours < other.hours() or (self._hours == other.hours() and self._minutes < other.minutes()) or (
                self._hours == other.hours() and self._minutes == other.minutes() and self._seconds < other.seconds()
            )
        def __str__(self):
            return str(self._hours)+''+str(self._minutes)+''+str(self._seconds)+''
    第一题
    '''
    2. 请定义一个类,实现正文中描述的Date抽象数据类型。
    '''
    class Date:
        dan = (1, 3, 5, 7, 8, 10, 12)
        shuang = (4, 6, 9, 11)
    
        @staticmethod
        def month_day(year, month):
            if month in Date.dan:
                month_day = 31
            elif month in Date.shuang:
                month_day = 30
            elif (year % 4 == 0 and year % 100 != 0)  or (year % 400 == 0) or (year % 3200 == 0 and year % 172800 == 0)and month == 2:
                month_day = 29
            else:
                month_day = 28
            return month_day
    
        def __init__(self, year, month=1, day=1):
            self._year = year
            self._month = month
            self._day = day
            self._leap_year = 0
            if (self._year % 4 == 0 and self._year % 100 != 0)  or (self._year % 400 == 0) or (self._year % 3200 == 0 and self._year % 172800 == 0):
                self._leap_year = 1
    
        def year(self):
            return self._year
    
        def month(self):
            return self._month
    
        def day(self):
            return self._day
    
        def __sub__(self, other):
            if self._year < other.year():
                raise ValueError('date1 < date1, not sub')
            oday = 0
            print('oday1',oday,other.month())
            for i in range(1,other.month()):
                oday += Date.month_day(self._year, i)
            oday += other.day()
            sday = 0
            for j in range(other.year(), self._year):
                if (j % 4 == 0 and j % 100 != 0) or (j % 400 == 0) or (j % 3200 == 0 and j % 172800 == 0):
                    sday += 366
                else:
                    sday += 365
            for m in range(1,self._month):
                sday += Date.month_day(self._year, m)
            sday += self._day
            return sday - oday
    
        def after_day(self, n):
            month_day = Date.month_day(self._year, self._month)
            if self._day + n <= month_day :
                return Date(self._year, self._month, self._day+n)
            else:
                n -= (month_day - self._day)
            month = self._month + 1
            year = self._year
            if month == 13:
                month = 1
                year += 1
    
            while n > Date.month_day(year, month):
                if month == 13:
                    month = 1
                    year += 1
                for i in range(month, 13):
                    month_day = Date.month_day(year, month)
                    if n < month_day:
                        break
                    n -= month_day
                    month += 1
            day = n
            return Date(year, month, day)
    
        def num_date(self, n):
            return self.after_day(n-1)
    
        def adjust(self, n):
            if n >=0 :
                return self.after_day(n)
            else:
                month_day = Date.month_day(self._year, self._month)
                if self._day + n > 0:
                    return Date(self._year, self._month, self._day + n)
                else:
                    n += self._day
                month = self._month - 1
                year = self._year
                if month == 0:
                    month = 12
                    year -= 1
    
                while n + Date.month_day(year, month) <= 0:
                    if month == 0:
                        month = 12
                        year -= 1
                    for i in range(month, 0, -1):
                        month_day = Date.month_day(year, month)
                        if n + month_day > 0:
                            break
                        n += month_day
                        month -= 1
                day = month_day + n
                return Date(year, month, day)
    
        def __str__(self):
            return str(self._year) + '/' + str(self._month) + '/' + str(self._day)
    第二题
  • 相关阅读:
    PNG文件格式具体解释
    opencv2对读书笔记——使用均值漂移算法查找物体
    Jackson的Json转换
    Java实现 蓝桥杯VIP 算法训练 装箱问题
    Java实现 蓝桥杯VIP 算法训练 装箱问题
    Java实现 蓝桥杯VIP 算法训练 单词接龙
    Java实现 蓝桥杯VIP 算法训练 单词接龙
    Java实现 蓝桥杯VIP 算法训练 方格取数
    Java实现 蓝桥杯VIP 算法训练 方格取数
    Java实现 蓝桥杯VIP 算法训练 单词接龙
  • 原文地址:https://www.cnblogs.com/walle-zhao/p/11383835.html
Copyright © 2011-2022 走看看