1.接口类
from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): @abstractmethod def pay(self,money): pass class Wechatpay(Payment): def fuqian(self,money): print('微信支付了%s元'%money) p = Wechatpay() #不调就报错了
继承有两种用途:
一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
from abc import abstractmethod,ABCMeta class Payment(metaclass=ABCMeta): # 元类 默认的元类 type @abstractmethod def pay(self,money):pass # 没有实现这个方法 # 规范 :接口类或者抽象类都可以 # 接口类 支持多继承,接口类中的所有的方法都必须不能实现 —— java # 抽象类 不支持多继承,抽象类中方法可以有一些代码的实现 —— java class Wechat(Payment): def pay(self,money): print('已经用微信支付了%s元'%money) class Alipay(Payment): def pay(self,money): print('已经用支付宝支付了%s元' % money) class Applepay(Payment): def pay(self,money): print('已经用applepay支付了%s元' % money) def pay(pay_obj,money): # 统一支付入口 pay_obj.pay(money)
# tiger 走路 游泳 # swan 走路 游泳 飞 # oldying 走路 飞 from abc import abstractmethod,ABCMeta class Swim_Animal(metaclass=ABCMeta): @abstractmethod def swim(self):pass class Walk_Animal(metaclass=ABCMeta): @abstractmethod def walk(self):pass class Fly_Animal(metaclass=ABCMeta): @abstractmethod def fly(self):pass class Tiger(Walk_Animal,Swim_Animal): def walk(self): pass def swim(self): pass class OldYing(Fly_Animal,Walk_Animal):pass class Swan(Swim_Animal,Walk_Animal,Fly_Animal):pass # 接口类 刚好满足接口隔离原则 面向对象开发的思想 规范
2.抽象类
import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能 def read(self): '子类必须定义读功能' pass @abc.abstractmethod #定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass # class Txt(All_file): # pass # # t1=Txt() #报错,子类没有定义抽象方法 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #这样大家都是被归一化了,也就是一切皆文件的思想 # wenbenwenjian.read() # yingpanwenjian.write() # jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
# 抽象类 : 规范 # 一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现 # 多继承的情况 由于功能比较复杂,所以不容易抽象出相同的功能的具体实现写在父类中 # 抽象类还是接口类 : 面向对象的开发规范 所有的接口类和抽象类都不能实例化 # java : # java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题 # 但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题 # python # python中没有接口类 : # python中自带多继承 所以我们直接用class来实现了接口类 # python中支持抽象类 : 一般情况下 单继承 不能实例化 # 且可以实现python代码
3.多态
多态指的是一类事物有多种形态
动物有多种形态:人,狗,猪
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): #动物的形态之二:狗 def talk(self): print('say wangwang') class Pig(Animal): #动物的形态之三:猪 def talk(self): print('say aoao')
一 什么是多态动态绑定(在继承的背景下使用时,有时也称为多态性) 多态性是指在不考虑实例类型的情况下使用实例 在面向对象方法中一般是这样表述多态性: 向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。 也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。 比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同 peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是动物,只要是动物肯定有talk方法 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 peo.talk() dog.talk() pig.talk() #更进一步,我们可以定义一个统一的接口来使用 def func(obj): obj.talk()
4.封装
在python中用双下划线开头的方式将属性隐藏起来(设置成私有的) #其实这仅仅这是一种变形操作 #类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式: class A: __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到. #A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形 这种自动变形的特点: 1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。 2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。 3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。 这种变形需要注意的问题是: 1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N 2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的 #正常情况 >>> class A: ... def fa(self): ... print('from A') ... def test(self): ... self.fa() ... >>> class B(A): ... def fa(self): ... print('from B') ... >>> b=B() >>> b.test() from B #把fa定义成私有的,即__fa >>> class A: ... def __fa(self): #在定义时就变形为_A__fa ... print('from A') ... def test(self): ... self.__fa() #只会与自己所在的类为准,即调用_A__fa ... >>> class B(A): ... def __fa(self): ... print('from B') ... >>> b=B() >>> b.test() from A
# 广义上面向对象的封装 :代码的保护,面向对象的思想本身就是一种 # 只让自己的对象能调用自己类中的方法 # 狭义上的封装 —— 面向对象的三大特性之一 # 属性 和 方法都藏起来 不让你看见 class Person: __key = 123 # 私有静态属性 def __init__(self,name,passwd): self.name = name self.__passwd = passwd # 私有属性 def __get_pwd(self): # 私有方法 return self.__passwd #只要在类的内部使用私有属性,就会自动的带上_类名 def login(self): # 正常的方法调用私有的方法 self.__get_pwd() alex = Person('alex','alex3714') print(alex._Person__passwd) # _类名__属性名 print(alex.get_pwd()) # 所有的私有 都是在变量的左边加上双下划綫 # 对象的私有属性 # 类中的私有方法 # 类中的静态私有属性 # 所有的私有的 都不能在类的外部使用
class Foo: __key = 1 def __init__(self): pass def func(self): pass class Son(Foo): print(Foo._Foo__key) print(Foo.__key) s = Son() # print(s._Foo__key) # print(Foo.__dict__) # f = Foo() # print(f._Foo__key) # print(f._Foo.__key) # 会用到私有的这个概念de场景 #1.隐藏起一个属性 不想让类的外部调用 #2.我想保护这个属性,不想让属性随意被改变 #3.我想保护这个属性,不被子类继承 # class Room: # def __init__(self,name,length,width): # self.__name = name # self.__length = length # self.__width = width # def get_name(self): # return self.__name # def set_name(self,newname): # if type(newname) is str and newname.isdigit() == False: # self.__name =newname # else: # print('不合法的姓名') # def area(self): # return self.__length*self.__width # shang = Room('shang',1,2) # # print(jin.area()) # # print(jin.name) # shang.set_name('chun') # print(shang.get_name())
5.
在 Python 中会用到对象之间比较,可以用 ==,也可以用 is 。但是它们的区别是什么呢? is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象,占用的内存地址是否相同。莱布尼茨说过:“世界上没有两片完全相同的叶子”,这个is正是这样的比较,比较是不是同一片叶子(即比较的id是否相同,这id类似于人的身份证标识)。 == 比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了。这里比较的并非是同一片叶子,可能叶子的种类或者脉络相同就可以了。默认会调用对象的 __eq__()方法。 可以通过如下例子来区分比较下: >>> a = ["I", "love", "Python"] >>> b = a # a的引用复制给b,在内存中其实是指向了用一个对象 >>> b is a True >>> id(a) 46381384 >>> id(b) 46381384 # 当然,内容也肯定是相等的 >>> b == a True 可以发现b和a的内存地址是相同的,它们指向同一块内存,因而 is 和 == 的结果都为True。这是因为直接赋值都是赋值的引用,是引用,是引用,重要的事情说三遍。但如果不是通过引用赋值,而是通过切片赋值呢? # b通过切片操作重新分配了对象,但是值和a相同 >>> b = a[:] >>> b is a False >>> id(a) 48740680 >>> id(b) 48740680 >>> b == a # 但他们的值还是相等的 True 新建对象之后,b 和 a 指向了不同的内存,所以 b is a 的结果为False,而 b==a的结果为True。在这里,小编提一个问题,b[0] is a[0] 的结果呢? 答案是True。因为切片拷贝是浅拷贝,列表中的元素并未重新创建。不理解的同学请翻看之前的文章 Python中的浅拷贝与深拷贝。 通常,我们关注的是值,而不是内存地址,因此 Python 代码中 == 出现的频率比 is 高。但是什么时候用 is 呢? is 与 == 相比有一个比较大的优势,就是计算速度快,因为它不能重载,不用进行特殊的函数调用,少了函数调用的开销而直接比较两个整数 id。而 a == b 则是等同于a.__eq__(b)。继承自 object 的 __eq__ 方法比较两个对象的id,结果与 is 一样。但是多数Python的对象会覆盖object的 __eq__方法,而定义内容的相关比较,所以比较的是对象属性的值。 在变量和单例值之间比较时,应该使用 is。目前,最常使用 is 的地方是判断对象是不是 None。下面是推荐的写法: a is None 判断不是None的推荐写法是: a is not None Python会对比较小的整数对象进行缓存,下次用的时候直接从缓存中获取,所以is 和 == 的结果可能相同: >>> a = 1 >>> b = 1 >>> a is b True >>> a == b True 而看一下另外一段代码: >>> a = 257 >>> b = 257 >>> a is b False 这是什么原因呢? 注意,Python仅仅对比较小的整数对象进行缓存(范围为范围[-5, 256])缓存起来,而并非是所有整数对象。需要注意的是,这仅仅是在命令行中执行,而在Pycharm或者保存为文件执行,结果是不一样的,这是因为解释器做了一部分优化。 总结 1、is 比较两个对象的 id 值是否相等,是否指向同一个内存地址; 2、== 比较的是两个对象的内容是否相等,值是否相等; 3、小整数对象[-5,256]在全局解释器范围内被放入缓存供重复使用; 4、is 运算符比 == 效率高,在变量和None进行比较时,应该使用 is。
6.
什么是特性property property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值 import math class Circle: def __init__(self,radius): #圆的半径radius self.radius=radius @property def area(self): return math.pi * self.radius**2 #计算面积 @property def perimeter(self): return 2*math.pi*self.radius #计算周长 c=Circle(10) print(c.radius) print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 print(c.perimeter) #同上 ''' 输出结果: 314.1592653589793 62.83185307179586 ''' #注意:此时的特性area和perimeter不能被赋值 c.area=3 #为特性area赋值 ''' 抛出异常: AttributeError: can't set attribute ''' 为什么要用property 将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则 除此之外,看下 ps:面向对象的封装有三种方式: 【public】 这种其实就是不封装,是对外公开的 【protected】 这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开 【private】 这种封装对谁都不公开 python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现 class Foo: def __init__(self,val): self.__NAME=val #将所有的数据属性都隐藏起来 @property def name(self): return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置) @name.setter def name(self,value): if not isinstance(value,str): #在设定值之前进行类型检查 raise TypeError('%s must be str' %value) self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME @name.deleter def name(self): raise TypeError('Can not delete') f=Foo('egon') print(f.name) # f.name=10 #抛出异常'TypeError: 10 must be str' del f.name #抛出异常'TypeError: Can not delete' 一个静态属性property本质就是实现了get,set,delete三种方法 class Foo: @property def AAA(self): print('get的时候运行我啊') @AAA.setter def AAA(self,value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA class Foo: def get_AAA(self): print('get的时候运行我啊') def set_AAA(self,value): print('set的时候运行我啊') def delete_AAA(self): print('delete的时候运行我啊') AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应 f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA 怎么用? class Goods: def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价
## property---- 属性 查看 修改 删除 # class Person: # def __init__(self,name,high,weight): # self.name = name # self.high = high # self.weight = weight # # self.bmi = weight / high ** 2 # @property # def bmi(self): # return self.weight / self.high ** 2 # # # shang = Person('shang',1.7,75) # print(shang.bmi) # class Person: # def __init__(self,name): # self.__name = name # @property # def name(self): # return self.__name + 'sb' # @name.setter # def name(self,newname): # self.__name = newname # shang = Person('shang') # print(shang.name) # shang.name = 'chun' # print(shang.name) # class Goods: # discount = 0.2 # def __init__(self,name,price): # self.name = name # self.__price = price # @property # def price(self): # return self.__price * Goods.discount # apple = Goods('苹果',5) # print(apple.price) class Person(P): def __init__(self,name): self.__name = name @property def name(self): return self.__name @name.deleter def name(self): del self.__name p = Person('shang') print(p.name) del p.name print(p.name)
7.
# method 方法 # staticmathod 静态的方法 *** # classmethod 类方法 **** # 类的操作行为 # class Goods: # __discount = 0.8 # def __init__(self,name,price): # self.name = name # self.__price = price # @property # def price(self): # return self.__price * Goods.__discount # @classmethod # 把一个方法 变成一个类中的方法,这个方法就直接可以被类调用,不需要依托任何对象 # def change_discount(cls,new_discount): # 修改折扣 # cls.__discount = new_discount # apple = Goods('苹果',5) # print(apple.price) # Goods.change_discount(0.5) # Goods.change_discount(Goods) # print(apple.price) # 当这个方法的操作只涉及静态属性的时候 就应该使用classmethod来装饰这个方法
# java class Login: def __init__(self,name,password): self.name = name self.pwd = password def login(self):pass @staticmethod def get_usr_pwd(): # 静态方法 usr = input('用户名 :') pwd = input('密码 :') Login(usr,pwd) Login.get_usr_pwd() # 在完全面向对象的程序中, # 如果一个函数 既和对象没有关系 也和类没有关系 那么就用staticmethod将这个函数变成一个静态方法 # 类方法和静态方法 都是类调用的 # 对象可以调用类方法和静态方法么? 可以 一般情况下 推荐用类名调用 # 类方法 有一个默认参数 cls 代表这个类 cls # 静态方法 没有默认的参数 就象函数一样
很多人在学完了python的class机制之后,遇到一个生产中的问题,还是会懵逼,这其实太正常了,因为任何程序的开发都是先设计后编程,python的class机制只不过是一种编程方式,如果你硬要拿着class去和你的问题死磕,变得更加懵逼都是分分钟的事,在以前,软件的开发相对简单,从任务的分析到编写程序,再到程序的调试,可以由一个人或一个小组去完成。但是随着软件规模的迅速增大,软件任意面临的问题十分复杂,需要考虑的因素太多,在一个软件中所产生的错误和隐藏的错误、未知的错误可能达到惊人的程度,这也不是在设计阶段就完全解决的。 所以软件的开发其实一整套规范,我们所学的只是其中的一小部分,一个完整的开发过程,需要明确每个阶段的任务,在保证一个阶段正确的前提下再进行下一个阶段的工作,称之为软件工程 面向对象的软件工程包括下面几个部: 1.面向对象分析(object oriented analysis ,OOA) 软件工程中的系统分析阶段,要求分析员和用户结合在一起,对用户的需求做出精确的分析和明确的表述,从大的方面解析软件系统应该做什么,而不是怎么去做。面向对象的分析要按照面向对象的概念和方法,在对任务的分析中,从客观存在的事物和事物之间的关系,贵南出有关的对象(对象的‘特征’和‘技能’)以及对象之间的联系,并将具有相同属性和行为的对象用一个类class来标识。 建立一个能反映这是工作情况的需求模型,此时的模型是粗略的。 2 面向对象设计(object oriented design,OOD) 根据面向对象分析阶段形成的需求模型,对每一部分分别进行具体的设计。 首先是类的设计,类的设计可能包含多个层次(利用继承与派生机制)。然后以这些类为基础提出程序设计的思路和方法,包括对算法的设计。 在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述 3 面向对象编程(object oriented programming,OOP) 根据面向对象设计的结果,选择一种计算机语言把它写成程序,可以是python 4 面向对象测试(object oriented test,OOT) 在写好程序后交给用户使用前,必须对程序进行严格的测试,测试的目的是发现程序中的错误并修正它。 面向对的测试是用面向对象的方法进行测试,以类作为测试的基本单元。 5 面向对象维护(object oriendted soft maintenance,OOSM) 正如对任何产品都需要进行售后服务和维护一样,软件在使用时也会出现一些问题,或者软件商想改进软件的性能,这就需要修改程序。 由于使用了面向对象的方法开发程序,使用程序的维护比较容易。 因为对象的封装性,修改一个对象对其他的对象影响很小,利用面向对象的方法维护程序,大大提高了软件维护的效率,可扩展性高。 在面向对象方法中,最早发展的肯定是面向对象编程(OOP),那时OOA和OOD都还没有发展起来,因此程序设计者为了写出面向对象的程序,还必须深入到分析和设计领域,尤其是设计领域,那时的OOP实际上包含了现在的OOD和OOP两个阶段,这对程序设计者要求比较高,许多人感到很难掌握。 现在设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这个5个阶段的工作不是由一个人从头到尾完成的,而是由不同的人分别完成,这样OOP阶段的任务就比较简单了。程序编写者只需要根据OOd提出的思路,用面向对象语言编写出程序既可。 在一个大型软件开发过程中,OOP只是很小的一个部分。 对于全栈开发的你来说,这五个阶段都有了,对于简单的问题,不必严格按照这个5个阶段进行,往往由程序设计者按照面向对象的方法进行程序设计,包括类的设计和程序的设计
1.面向对象的程序设计看起来高大上,所以我在编程时就应该保证通篇class,这样写出的程序一定是好的程序(面向对象只适合那些可扩展性要求比较高的场景) 2.很多人喜欢说面向对象三大特性(这是从哪传出来的,封装,多态,继承?漏洞太多太多,好吧暂且称为三大特性),那么我在基于面向对象编程时,我一定要让我定义的类中完整的包含这三种特性,这样写肯定是好的程序 好家伙,我说降龙十八掌有十八掌,那么你每次跟人干仗都要从第一掌打到第18掌这才显得你会了是么:面对敌人,你打到第三掌对方就已经倒下了,你说,不行,你给老子起来,老子还没有show完... 3.类有类属性,实例有实例属性,所以我们在定义class时一定要定义出那么几个类属性,想不到怎么办,那就使劲的想,定义的越多越牛逼 这就犯了一个严重的错误,程序越早面向对象,死的越早,为啥面向对象,因为我们要将数据与功能结合到一起,程序整体的结构都没有出来,或者说需要考虑的问题你都没有搞清楚个八九不离十,你就开始面向对象了,这就导致了,你在那里干想,自以为想通了,定义了一堆属性,结果后来又都用不到,或者想不通到底应该定义啥,那就一直想吧,想着想着就疯了。
抽象/实现 抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。 对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。 封装/接口 封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。 注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来” 真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明 (注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在) 合成 合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。 派生/继承/继承结构 派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。 继承描述了子类属性从祖先类继承这样一种方式 继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。 泛化/特化 基于继承 泛化表示所有子类与其父类及祖先类有一样的特点。 特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。 多态与多态性 多态指的是同一种事物的多种状态:水这种事物有多种不同的状态:冰,水蒸气 多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。 冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样 自省/反射 自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__
8,isinstance(obj,cls)检查是否obj是否是类 cls 的对象
issubclass(sub, super)检查sub类是否是 super 类的派生类
# # class Foo(object): # pass # # # obj = Foo() # # print(isinstance(obj, Foo)) # print(isinstance(Foo, Foo))
class Foo(object): pass class Bar(Foo): pass class B(): pass print(issubclass(Bar, Foo)) print(issubclass(Bar, Bar)) print(issubclass(Bar, B)) print(issubclass(Foo, Foo)) print(issubclass(Foo, object))
9.
# 通过反射 # 对象名 获取对象属性 和 普通方法 # 类名 获取静态属性 和类方法 和 静态方法 # 普通方法 self # 静态方法 @staticmethod # 类方法 @classmethod # 属性方法 @property class Teacher: dic = {'学生信息':'show_student','教师信息':'show_teacher'} def show_student(self): print(123) def show_teacher(self): print(456) @classmethod def func(cls): print(789) alex = Teacher() # alex.show_student() for k in Teacher.dic: print(k) key = input('输入需求:') # print(Teacher.dic[key]) if hasattr(alex,Teacher.dic[key]): func = getattr(alex,Teacher.dic[key]) print(func) print(func()) # ret = getattr(Teacher,'dic') # if hasattr(Teacher,'dic4'): # ret = getattr(Teacher,'dic4') # print(ret) # alex = Teacher() # ret = getattr(alex,'show_student') # print(ret) # print(ret())
# 反射 : 是用字符串类型的名字 去操作 变量 # name = 1 # eval('print(name)') # 安全隐患 # 反射 就没有安全问题 # 反射 : 是用字符串类型的名字 去操作 变量 # 反射对象中的属性和方法 # hasattr getattr setattr delattr # class A: # def func(self): # print('in func') # # a = A() # a.name = 'alex' # a.age = 63 # # 反射对象的属性 # ret = getattr(a,'name') # 通过变量名的字符串形式取到的值 # print(ret) # print(a.__dict__) # 变量名 = input('>>>') # func # print(getattr(a,变量名)) # print(a.__dict__[变量名]) # # # 反射对象的方法 # a.func() # ret = getattr(a,'func') # ret() # # class A: # price = 20 # @classmethod # def func(cls): # print('in func') # # 反射类的属性 # # A.price # print(getattr(A,'price')) # # # 反射类的方法 :classmethod staticmethod # # A.func() # if hasattr(A,'func'): # getattr(A,'func')() #模块 # import my # 反射模块的属性 # print(my.day) # print(getattr(my,'day')) # 反射模块的方法 # getattr(my,'wahaha')() # 内置模块也能用 # time # asctime # import time # print(getattr(time,'time')()) # print(getattr(time,'asctime')()) # def qqxing(): # print('qqxing') # year = 2018 # import sys # # print(sys.modules['__main__'].year) # # 反射自己模块中的变量 # # print(getattr(sys.modules['__main__'],'year')) # # # 反射自己模块中的函数 # # getattr(sys.modules['__main__'],'qqxing')() # 变量名 = input('>>>') # print(getattr(sys.modules[__name__],变量名)) # 要反射的函数有参数怎么办? # print(time.strftime('%Y-%m-%d %H:%M:S')) # print(getattr(time,'strftime')('%Y-%m-%d %H:%M:S')) # 一个模块中的类能不能反射得到 # import my # print(getattr(my,'C')()) # if hasattr(my,'name'): # getattr(my,'name') # 重要程度半颗星 # setattr 设置修改变量 class A: pass a = A() setattr(a,'name','nezha') setattr(A,'name','alex') print(A.name) print(a.name) # delattr 删除一个变量 delattr(a,'name') print(a.name) delattr(A,'name') print(a.name)
10.
# 内置的类方法 和 内置的函数之间有着千丝万缕的联系 # 双下方法 # obj.__str__ str(obj) # obj.__repr__ repr(obj) # class Teacher: # def __init__(self,name,salary): # self.name = name # self.salary = salary # def __str__(self): # return "Teacher's object :%s"%self.name # def __repr__(self): # return str(self.__dict__) # def func(self): # return 'wahaha' # nezha = Teacher('哪吒',250) # print(nezha) # 打印一个对象的时候,就是调用a.__str__ # print(repr(nezha)) # print('>>> %r'%nezha) #a.__str__ --> object # object 里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址 # l = [1,2,3,4,5] # 实例化 实例化了一个列表类的对象 # print(l) # %s str() 直接打印 实际上都是走的__str__ # %r repr() 实际上都是走的__repr__ # repr 是str的备胎,但str不能做repr的备胎 # print(obj)/'%s'%obj/str(obj)的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串 # 如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。 # repr(),只会找__repr__,如果没有找父类的