就算你是特别聪明,也要学习,从头学起!——(俄国)屠格涅夫

本篇文章要说的主要是数据结构与算法和python中关于类(Class)以及异常(Error)的一些基础,虽然很简单,但是必须非常重视。只有在牢牢掌握了这些基础的前提下,我们才能学得更快,无论是找工作还是以后在工作岗位上,扎实的基础都会给我们带来事半功倍的效益。千里之行始于足下,我们在各行各业,无论涉及什么领域,都不要忘了基础的重要性。

开始正题!

抽象数据类型

按照抽象的思维,设计者在考虑一个程序部件时,首先应该给出一个清晰的边界,通过一套接口描述说明这一程序部分可用的功能,但并不限制功能的实现方法。从使用者的角度来看,一个程序部件实现了一种功能,如果适合实际需要,就可以通过其接口使用它,并不需要知道实现的具体细节。就像python函数一样,其也是一种功能部件,其头部定义了它的接口,描述了函数的名字及其对参数的要求。使用者只需要考虑函数的功能是否满足需要,保证调用式符合函数头部的需求,并不需要知道函数实现的任何细节。

抽象数据类型的基本思想是把数据定义为抽象的对数集合,只为它们定义可用的合法操作,并不暴露其内部实现的具体细节,不论是其数据的表示细节还是操作的实现细节。比如python里的str是一个典型例子,字符串对象有一种内部表示方式(无须对外宣布),人们用python编程序时并不依赖于实际的表示;str提供了一组操作供编程使用,每个操作具有明确的抽象意义,不依赖于内部的具体实现技术。

作为数据类型,特别是比较复杂的数据类型,有一个很重要的性质称为变动性,表示该类型的对象在创建之后是否允许变化。一旦创建之后不会变化的称为不变数据类型,这种类型在程序里只能构建新对象或者取得已有对象的特性,不能修改。反之,可变数据类型的对象不发生改变,但是内容可变,下面经常将不变数据类型和可变数据类型简称为不变类型可变类型。在python中的number、str、tuple和frozenset是不变数据类型,而list、set和dict是可变数据类型。

说到可变数据类型和不变数据类型,就顺便说一说python中深拷贝浅拷贝问题吧

作为‘面向对象’的语言,python的一切变量都是对象,变量在存储时,采用了引用语义的方式,存储的是在这个变量的内存地址,而不是变量本身,这点和值语义不一样的地方是,值语义的语言在赋值时,存储的是变量本身。

比如在值语义中,a=1;b=a,那么b存储了a的值1,b和a两个变量就不会有联系了。在引用语义中,a=1;b=a,a变量存储的是1的内存地址,a赋值b的时候,b得到的也是a的内存地址,那么a和b将指向相同的内存地址。

python是引用语义机制,因此赋值操作不会开辟新的内存空间,它只是复制了源对象的引用罢了。

引用语义:两个变量赋值时,只是指向同一个内存引用

搞懂了python的赋值机制,接下来谈一谈浅拷贝问题。拷贝有三种操作方式:切片操作,工厂函数以及copy函数。

完全切片操作:

list1=[1,2,3,4,5]
list2=list1[:]
print(list1,id(list1))
print(list2,id(list2))[1, 2, 3, 4, 5] 1886748369416
[1, 2,3,4,5] 1886748369480list1[1]=[1,2]
print(list1,id(list1))
print(list2,id(list2))[1, [1, 2], 3, 4, 5] 1886748369416
[1, 2,3,4,5] 1886748369480

从上面可以分析出:1.列表在改变自身元素时,内容发生改变,但不会改变内存地址;2.切片操作会另辟一个新的内存空间给变量list2,list1和list2之间不再有联系,不会相互影响。

工厂函数:

list1=[1,2,3,4,5]
list2=list(list1)
print(list1,id(list1))
print(list2,id(list2))[1, 2, 3, 4, 5] 3113769918984
[1, 2, 3, 4, 5] 3113770401416

list1和list2内容和类型一样,但内存地址不同,表示list2新开辟一个内存空间存储list1的复制,后面无论list1和list2如何操作都不会相互影响。

copy函数:

import copy
list1=[1,2,3,4,[5,6]]
list2=list1
list3=copy.copy(list1)    #浅拷贝  list3=list1.copy() (python自建函数)
list4=copy.deepcopy(list1)  #深拷贝
list1.append(8)
list1[4].append(7)
print(list2);print(list3);print(list4)[1, 2, 3, 4, [5, 6, 7], 8]
[1, 2, 3, 4, [5, 6, 7]]
[1, 2, 3, 4, [5, 6]]

  1. 将list1赋值给list2,即list2和list1引用同一个内存地址,因此该内存地址内容发生改变,则两个变量一起改变。
  2. list3是对list1的浅拷贝,即拷贝了list1这个对象(两个list内存地址不同,但是list1和list3中每个元素的内存地址相同),因此在list1外层发生改变时,list3并不会改变,但是当list1中的元素(嵌套列表)发生改变时,由于列表是可变对象,内容改变内存地址不变,list1和list3的元素共享地址,因此list3中嵌套列表元素同样发生改变。
  3. 深拷贝是另辟一个新的内存空间,拷贝了整个list1和其中元素。因此无论list1中发生何种改变,都不会影响到list3。

python类

执行一个类定义将创建一个类对象,这种对象主要支持两种操作:属性访问和实例化(创建这个类的实例对象),类的属性分为数据属性函数属性,在调用属性时的操作为classname.att or classname.method()。

python基本类的创建这里就不多说啦,基础不够的朋友去看看廖雪峰官方网站的python教程噢。这里总结一些比较不常见的但仍然很重要的知识点。

类里的修饰符

  • def行前加修饰符@staticmethod,静态方法实际就是普通函数,只是由于某种原因需要定义在类中,静态方法的参数可以根据需要定义,不需要有self参数,但是可以用类名去调用这个静态函数。例如classname.staticmethod(),或者self.staticmethod()。无论采用哪种调用形式,参数表里都必须为每个形参提供实参,这里没有自动使用的self。
  • def行前加修饰符@classmethod,类方法,调用类的参数,类方法也是类的属性,可以以属性访问的形式调用。类方法执行时,调用它的类将自动约束到方法的cls参数(类参数),可通过这个参数访问其他属性。人们通常用类方法实现于本类的所有对象有关的操作。
class Countable:counter=0     #类参数def __init__(self):Countable.counter+=1@classmethoddef get_count(cls):      #类方法自动绑定类属性cls,类似于selfreturn Countable.counter
'''类参数不在初始化函数中,因此每实例化一次该类,类参数就会+1,
并刷新原类的值,该功能可以计数实例化了多少次该类。'''

  • def行前加修饰符@property,装饰器,利用间接的代码对类里的参数进行检查
#传统方式
class Student(object):def get_score(self):return self._scoredef set_score(self, value):      #传统方法检查参数必须通过调用函数方法来实现if not isinstance(value, int):raise ValueError('score must be an integer!')if value < 0 or value > 100:raise ValueError('score must between 0 ~ 100!')self._score = value#加有装饰器
class Student:@propertydef score(self):   #return self.__score@score.setter         定义装饰器属性的函数方法def score(self,value):if not isinstance(value,int):raise ValueError('score must be an integer')if value<0 or value>100:raise ValueError('score is out of range')
'''第一个score(self)是get方法,用@property装饰,第二个score(self, score)是set方法,
用@score.setter装饰,@score.setter是前一个@property装饰后的副产品。'''l=Student()
l.score=999
print(l.score)       ValueError: score is out of range

私有变量

我们知道类创建的属性是可以通过外部访问和修改的,如果有时候有些很重要的属性不能访问和修改时,必须得限制外部的访问和修改权利。这是一种信息隐藏机制。一般属性加两个下划线,例如self._attr,这就是私有属性,当调用类对象时,在外部是无法找到和访问到该属性的,不过一般我们不会这么做。当你有不想让别人访问的属性时,可以在属性前加1个下划线,self._attr,这种写法实际上仍然是有外部访问和修改的权限的,但是告诉别人这个属性不应该被访问和修改,因此全靠自觉

继承、基类和派生类

继承的主要作用有2个:一是可以基于已有的类定义新类,通过继承的方式复用已有类的功能,重复利用已有的代码,减少定义新类的工作量,简化新功能的开发,提高工作效率;另一个作用是建立一组类之间的继承关系,利用这种关系可能更好地组织和构造复杂的程序。

通过继承定义出的新类称为所列已有类的派生类(子类),被继承的已有类则称为这个派生类的基类(父类)。派生类可以继承基类所有的属性和方法,也可以修改一些功能,可以根据需要扩充新功能(定义新的属性或函数)。

一个类可能是其他类的派生类,它有可能被用作基类去定义新的派生类,这样在一个程序里可能会根据继承关系形成一种层次结构。python有一个内置的类object,其中定义了一些所有类都需要的基本功能。如果一个类没有定义说明基类,那么该类就自动以object为基类,换句话说,任何自定义的类都是object的派生类。python内置函数issubclass()检查两个类是否具有继承关系,包括直接或间接地继承关系,返回bool值。

派生类常需要重新定义_init_函数,完成该实例的初始化,不仅要初始化自己定义的属性也需要定义所继承的父类的所有属性。

class basedclass:def __init__(self):pass
class subclass(basedclass):def __init__(self):basedclass.__init__(self,....)    #初始化父类的属性操作.....      #初始化子类的额外属性

标准函数super():super() 函数是用于调用父类(超类)的一个方法。super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

super().m()...,从该类的基类(可能存在多个基类,按照MRO顺序查找)开始搜索方法m()。或者super(class,self).m(),即在指定的基类去寻找方法。super()._init_(),初始化所有基类属性

python异常

如果程序中出现异常,无论是解释器发生异常或者是raise语句引发的异常,非常执行流程都会终止,解释器转入异常处理模式,查找能处理所发生异常的处理器。若找不到则会输出错误信息结束当前执行。python中常见异常有ValueError,TypeError,ZeroDivisionError等。

python中的异常都是类,运行中产生异常就是生成相应类的实例对象,全体内部异常类构成了一个树形结构,所有异常类的基类是BaseException,最主要的子类是Exception,内置异常类都是这个类的直接或简介派生类。如果自定义异常,就应该从系统异常类中选择一个合适的异常,派生出自己的异常类:

class RelationError(ValueError):pass

运行中发生的异常与处理器匹配按面向对象的方式处理。假设运行中发生的异常是e,如果一个异常处理器头部列有异常名E,且isinstance(e,E)为真,那么这个处理器就能捕捉并处理异常e。如果引发了RelationError异常,某个处理器头部列出了RelationError,或者ValueError或者Exception,该处理器就能捕捉这个异常,匹配ValueError的处理器还能捕捉和处理其他异常,匹配Exception的处理器能捕捉和处理各种主要异常。

异常的传播和捕捉:假设在函数f的执行中发生异常e,当前执行立即中断转入异常处理模式

  • 首先在发生异常的函数体中查找处理器:

    • 如果发生异常的语句位于一个try语句体里,首先顺序检查这个try语句后部的各except子句,检查是否存在能处理e的处理器。
    • 如果发生异常的try语句的所有异常处理器都不能处理e,解释器转去查看包围着该try语句外围try语句(如果存在),检查是否存在能与e匹配的异常处理器。
    • 如果e不能在函数f里处理,则f的执行异常终止,e在函数f这次执行的调用点重新引发,导致又一轮处理器查找工作。查找规则与上面一样。
  • 如果上面的查找过程在某一步找到了与e匹配的处理器,解释器就去执行该except子句的体。执行完该段代码后,解释器回到正常执行模式,从该异常处理器所在的try语句之后继续执行。
  • 上述查找过程可能导致函数一层层以异常方式退出,有可能一直退到当前模块的最上层也没有找到与之匹配的处理器:
    • 如果程序是在解释器的交互方式下执行,python解释器终止该模块执行并回到交互状态,输出错误信息后等待下一个命令。
    • 如果程序是自主执行,该程序立即终止。

标准异常类都是Exception的直接或间接派生类。

接下来介绍一些关于python中类的一些知识和技巧:

访问限制:私有属性

在前面已经提及到,设置为私有的属性在外部是无法获得访问和修改权限的,如果一定要访问和修改的话,就必须设定专门的方法。

class student:def __init__(self,name,score):self.__name=name      #私有属性,外部无法直接访问和修改self.__score=score    #私有属性,外部无法直接访问和修改def get_name(self):             # 外部无法访问私有属性,可通过定义get_name(),get_score()函数来调用return self.__namedef get_score(self):return self.__scoredef set_score(self,score):     # 外部无法修改私有属性,可通过定义set_name(),set_score()函数来调用self.__score = score

获取对象的信息

dir():#返回对象的所有属性和方法
hasattr,getattr,setattr:#判断是否有,得到,添加或设置某属性L=class()
hasattr(L,'x')  #L类是否有x属性
getattr(L,'x') #返回L类x属性的值,等价于L.x
setattr(L,'x',4) #将L类x属性的值设置为4  等价于L.x=4

限制类在外部动态添加属性和方法(_slots_)

#__slots__  限制类在外部动态添加属性和方法
class father:__slots__ = ('name','age')     #只允许在外部动态添加指定的属性
f=father();f.name='job'            #在外部动态添加属性和类
f.score=100        #添加属性失败
print(f.name);print(f.score)     #AttributeError,无法添加score属性,被__slot__限制住了class son(father):__slots__ = father.__slots__       #子类继承父类的__slot__必须特别指定

定制类:几种特殊函数在类中的使用

__str__

class F:def __init__(self,name,age):self.name=nameself.age=agedef double_age(self):self.age=self.age*self.agereturn self.agedef __str__(self):    #为属性或函数方法的打印而服务return 'your name is %s,age is %d'%(self.name,self.double_age())print(F('miaceal',27))  #实例化,直接print就行,打印__str__函数返回值

__iter__/__next__

如果类想被用于迭代循环,那么该方法返回一个迭代对象,然后利用__next__的方法来不断得到循环的值

class Feibo:    #斐波那契数列def __init__(self):self.a,self.b=0,1def __iter__(self):    #返回一个迭代对象,将类实例改为一个可迭代对象return selfdef __next__(self):self.a,self.b=self.b,self.a+self.bif self.a>20:raise StopIteration    #迭代停止条件return self.afor n in Feibo():print(n)

__getitem__

__iter__虽然将类变为可迭代对象,但始终不是列表,不能进行切片引用,此时用到__getitem__,注意这是适用于单个切片索引。

class Feibo:    #斐波那契数列def __getitem__(self, item):a,b=1,1for x in range(item):a,b=b,a+breturn aprint(Feibo()[2])     #单个切片索引,如果是多索引,例如[1:6],上面的__getitem__会报错

要想可以多索引可以对__getitem__类进行改进:

class Feibo:def __getitem__(self, item):if isinstance(item,int):     #分别对切片索引类型进行判断a,b=1,1for i in range(item):a,b=b,a+breturn aif isinstance(item,slice):   #对切片索引类型进行判断start=item.startstop=item.stopstep=item.stepif start is None:start=0a,b=1,1L=[]for i in range(stop):if i>=start:L.append(a)a,b=b,a+breturn Lprint(Feibo()[3:8])

__getattr__

当类里没有某属性并且在外面动态调用时会出错,此时需要利用__getattr__函数来增添未有的属性。

#传统方式
class fun:def __init__(self,name):self.name=name
l=fun('micheal')
print(l.name)       #micheal
print(l.score)       #AttributeError: 'fun' object has no attribute 'score'
l.score=37          #外部动态添加score属性
print(l.score)       #访问新添加的属性  :37#__getattr__函数
class fun:def __init__(self,name):self.name=namedef __getattr__(self, item):    #构造__getattr__函数,当访问类中所没有的属性时,返回函数方式if item is 'score':item=99return itemraise AttributeError('no attr named '%s' in class'%item)   #在__getattr__函数中仍然没有的属性就会抛出异常
l=fun('micheal')
print(l.name)    #micheal
print(l.score)    #99
print(l.fei)      #定义了__getattr__函数的类在没有定义某属性,但在外部仍然调用到时,会返回None,此时一般抛出错误

程序的调试和错误处理

# 程序的调试与错误处理
# try ...except...finally...
'''用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,
即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕'''try:r=10/0   #发现错误print('result:',r)  #后续自动截断,不会再执行except Exception as e:   #except 捕捉到错误信息并执行print('Error:',e)           #执行exceptfinally:print('finally...')   #执行finally''' 使用try...except捕获错误还有一个巨大的好处,就是可以跨越多层调用,
比如函数main()调用foo(),foo()调用bar(),结果bar()出错了,这时,只要main()捕获到了,就可以处理:'''import logging      #导入错误日志函数def foo(s):return 10 / int(s)def bar(s):return foo(s) * 2def main():try:                         #在主函数中调用try,可以检查另外两个子函数的错误bar('0')except Exception as e:logging.exception(e)   #记录错误信息,并返回错误堆栈print('Error:', e)finally:print('finally...')main()# 记录错误信息from functools import reduceimport loggingdef str2num(s):return int(s)def calc(exp):ss = exp.split('+')ns = map(str2num, ss)return reduce(lambda acc, x: acc + x, ns)def main():try:r = calc('100 + 200 + 345')print('100 + 200 + 345 =', r)r = calc('99 + 88 + 7.6')print('99 + 88 + 7.6 =', r)except Exception as e:print('Error:',e)logging.exception(e)main()# logging有相应的四个级别一般在首几行进行配置
# import logging
# logging.basicConfig(level=logging.INFO)  #另外还有lWARNING, DEBUG,ERROR模式
# 对应于日志函数:logging.info(),logging.error(),logging.warning(),logging.debug()
# s='0';n=int(s);
# logging.info('n=%d'%n)
# print(10/n)


参考书籍:《数据结构与算法—python语言描述》—裘宗燕

python @修饰符_数据结构与算法之8——抽象数据类型与python类相关推荐

  1. python思想读后感_数据结构与算法:Python语言描述读后感1000字

    <数据结构与算法:Python语言描述>是一本由裘宗燕著作,机械工业出版社出版的平装图书,本书定价:CNY 45.00,页数:343,特精心从网络上整理的一些读者的读后感,希望对大家能有帮 ...

  2. 数据结构python课后答案_数据结构与算法:Python语言描述 1~5章课后习题

    数据结构与算法:Python语言描述 1~5章课后习题 发布时间:2018-07-19 20:42, 浏览次数:1885 , 标签: Python MarkDown语法写的,不知道为啥上传到CSDN不 ...

  3. python最大分词_中文分词算法之最大正向匹配算法(Python版)

    最大匹配算法是自然语言处理中的中文匹配算法中最基础的算法,分为正向和逆向,原理都是一样的. 正向最大匹配算法,故名思意,从左向右扫描寻找词的最大匹配. 首先我们可以规定一个词的最大长度,每次扫描的时候 ...

  4. mooc数据结构与算法python版期末测验_中国大学MOOC(慕课)_数据结构与算法Python版_测试题及答案...

    中国大学MOOC(慕课)_数据结构与算法Python版_测试题及答案 更多相关问题 采用fopen()函数打开文件,支持文件读取的参数有: [简答题]简单阐述高分子材料热-机械特征及成型加工的关系,并 ...

  5. python修饰符的理解_python函数修饰符@的使用方法解析

    这篇文章主要介绍了python函数修饰符@的使用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 python函数修饰符@的作用是为现有函数增 ...

  6. python修饰符用法_c#教程之C#语言中的修饰符汇总

    https://www.xin3721.com/eschool/python.html 修饰符是用于限定类型以及类型成员的申明的一种符号. 下面主要从C#中的访问修饰符,作用于类和结构的修饰符,用在方 ...

  7. 嵌入式团队培训_数据结构和算法概述

    嵌入式团队培训_数据结构与算法概述 要求:理解并记忆即可,会求解算法的时间复杂度 一:数据结构 1.逻辑结构: 2.物理结构 3.抽象数据类型 二:算法 1.算法的五个基本特征: 2.算法设计的要求 ...

  8. python数据结构与算法分析_数据结构与算法(Python版)

    为什么研究数据结构与算法 本周带大家进入Python版数据结构与算法的学习.想必大家都听过"算法"一词,算法的学习对编程者来说是至关重要的.首先我们先了解一下为什么要研究数据结构与 ...

  9. python数据结构视频百度云盘_数据结构与算法Python视频领课

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 课程简介: 本课程包含Python编程基础的基本语法及变量,基本数据结构,Code Structure,Function.让学生在学会Python基础的同 ...

最新文章

  1. 判断是否在数组中,若在输入其下标,否则输入-1
  2. linux pti性能影响,Linux修正内核:Intel打补丁性能狂降、AMD不受影响
  3. 揭秘 | 连续3年支撑双11,阿里云神龙如何扛住全球流量洪峰?
  4. scala中的二维数组_Scala中的多维数组
  5. python海贼王logo_Python 实现的下载op海贼王网的图片(网络爬虫)
  6. socket编程简单Demo讲解及源码分享(C# Winform 内网)
  7. vue3 @/cli脚手架搭建项目
  8. 解决markdown快捷键在输入法和chrome浏览器下的冲突问题
  9. windows ghost系统下载
  10. 车载 DCDC 电源模块
  11. 今天买了正版的beyondcompare 4.0
  12. MyDLNote-High-Resolution: CooGAN: 协同GAN网络,高分辨率面部属性的高效记忆框架
  13. vs2017无法打开文件atls.lib问题
  14. Python中 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape错误解决方法
  15. 【这不是经验】VS编译器初始化报错
  16. 驱动开发笔记5—驱动对象、设备对象、IRP和派遣函数
  17. 为什么去开发一个MLSQL
  18. 2190 悼念512汶川大地震遇难同胞——重建希望小学
  19. 布林通道 Bollinger Bands——非炒股向个人学习笔记
  20. Android network — DHCP协议详解

热门文章

  1. 没有ggplot这个函数_JavaScript学习笔记(四)——函数基础
  2. 菜鸟学习笔记:Java提升篇11(Java动态性1——注解与反射)
  3. java 输入流可以合并吗_Java 使用IO流实现大文件的分割与合并实例详解
  4. mysql 设置数据库只读_如何设置mysql数据库为只读
  5. mysqld_safemysqld区别
  6. 新年第一篇!西南民族大学第十届校赛(同步赛)
  7. (办公)eclipse连接github cannot open git-upload-pack(git-receive-pack)
  8. 微信小程序setData的回调方法
  9. C#跨窗体传值的几种方法分析第三版
  10. JavaC#语法差别