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)
    第二题
  • 相关阅读:
    PSP ISO游戏运行必备工具:ISO TOOL 1.970 功能一览&图文教程
    Linux防火墙(原书第3版) 电子书籍
    iptables的相关概念和数据包的流程(图)
    oracle数据库远程连接服务器配置tnsnames
    编程感悟
    工作任务三 打印表单数据
    UltraWebTree的使用心得
    DropDownList应用
    使用UltraWebTree时,如何在刷新后展开之前选中的节点,并绑定相关数据
    webgrid 添加行是不允许相同
  • 原文地址:https://www.cnblogs.com/walle-zhao/p/11383835.html
Copyright © 2011-2022 走看看