java自学教程|www.konglongmei.com

作者: 简单350
查看: 47|回复: 0

more +社区更新Forums

more +随机图赏Gallery

[技术知识] 03-深入类和对象

[技术知识] 03-深入类和对象

[复制链接]
简单350 | 显示全部楼层 发表于: 2019-12-3 00:16:28
简单350 发表于: 2019-12-3 00:16:28 | 显示全部楼层 |阅读模式
查看: 47|回复: 0

你还没有注册,无法下载本站所有资源,请立即注册!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
一、深入类和对象

1.1、鸭子类型和多态
维基百科中的解释为:
  鸭子类型(英语:duck typing)在程序设计中是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。这个概念的名字来源于由詹姆斯·惠特科姆·莱利提出的鸭子测试,“鸭子测试”可以这样表述:
  “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。
  1. class Cat():    def say(self):        print("I am a cat")class Dog():    def say(self):        print("I am a dog")class Duck():    def say(self):        print("I am a duck")animal_list = [Cat,Dog,Duck]for animal in animal_list:    animal().say()#实例化对象,在调用say方法  三个类实现同一个方法名,这就是多态。然后可以将这些类归为一种类型(鸭子类型)                  #python中的魔法函数充分也利用了鸭子类型的特性,可以在任一类中定义name_list = ["list1","list2"]name_list1 = ["love","python"]name_tuple = (3,4)name_set = set()name_set.add(5)name_set.add(6)name_list.extend(name_set)   #参数name_set:['list1', 'list2', 5, 6]参数name_tuple:['list1', 'list2', 3, 4] print(name_list)              # 参数:name_list1:['list1', 'list2', 'love', 'python']"""这里说的是只要传入的参数是一个可迭代的类型就可以,就连我们自定义的类将类的魔法函数__getitem__(返回 )、__iter__就可以变成可迭代的,都可以传入 def extend(self, *args, **kwargs): # real signature unknown        Extend list by appending elements from the iterable.         pass"""#首先这三个类里面都包含了这个say()方法,如果在JAVA里边,要实现多态的话,需要继承父类在覆盖父类的方法实现多态。# 例如:一般情况先定义一个父类Animal,然后这个Animal有一个say()方法。# 然后在写其他类例如上面的Cat类,Cat类继承Animal类,然后重写say()方法。# 然后指定类型实例化这个Cat对象,在python中不需要指定类型,在JAVA中(静态语言)必须指定类型,#这是动态语言和静态语言最大的区别。在python中都要做的一件事就是每个对象下都要写这个say()方法
复制代码
1.2、抽象基类(abc)
  python里边的抽象基类,是不能够实例化的。python是动态语言,动态语言是没有变量的类型的。在python中变量只是一个符号而已,这个符号可以指向任何类型的对象。动态语言缺少编译时检查错误的环境,在python中编写代码是很难发现错误的,只有要运行解释器才能找到错误。这也是动态语言共有的一个缺陷。python信奉的是鸭子类型,鸭子类型贯穿于整个面向对象之中。抽象基类是什么意思?在这个基础的类当中,设定好一些方法,然后所有的继承这个基类的类,都必须覆盖这个抽象基类里面的方法。抽象基类是无法实例化的。
  1. ##################去检查某个类是否有某种方法#############################class Students(object):    def __init__(self,student_list):        self.student = student_list    def __len__(self):        return len(self.student)students = Students(["lishuntao","test","python"])# print(hasattr(students,"__len__"))#True# print(hasattr(students,"__getitem__"))#False##############################判定某个对象的类型#####################################from collections.abc import Sizedprint(isinstance(students,Sized))#True######################利用抽象基类实现接口的 强制规定###########################强制某些子类必须实现某些方法#实现了一个web框架,集成cache(redis,cache,memorychache)#需要设计一个抽象基类,指定子类必须实现某些方法#如何去模拟一个抽象基类呢?class  CacheBase():    def get(self,key):        raise NotImplementedError    def set(self,key,value):        raise NotImplementedError#用户在实现这个抽象基类的子类时候,必须实现这里面的两个方法class RedisCache(CacheBase):    passredis = RedisCache()#redis.get("key")#抛出异常raise NotImplementedError NotImplementedError#但这样做不好,我们需要刚初始化的时候就抛出异常,接下来就换成abc实现个人基类import abcclass Cache1Base(metaclass=abc.ABCMeta):    @abc.abstractmethod    def get(self,key):        pass    @abc.abstractmethod    def set(self,key,value):        passclass RedisCache1(Cache1Base):    passredis_cache1 = RedisCache1() #TypeError: Can't instantiate abstract class RedisCache1 with abstract methods get, set#利用抽象基类直接初始化抛出异常#在python当中已经实现了一些通用的抽象基类,放在from collections.abc import *
复制代码
  抽象基类不是用来继承的,它只是利用抽象基类来理解继承之间的关系,以及接口的定义,我们去使用的时候一定要用我们的鸭子类型,如果一定要用接口的话,那么推荐使用mixin多继承的方式去实现它。抽象基类使用的时候设计过度,反而不容易理解它。
1.3、isinstance和type的区别
  1. class A:    passclass B(A):    passb = B()print(isinstance(b,B)) #Trueprint(isinstance(b,A)) #Trueprint(type(b) is B) #True   is与==的区别,==判断值是否相等,is判断是不是同一个对象(id(b)地址是否一样)print(type(b),A) #False  ###########判断类型:为什么更推荐用isinstance而不是type?###############因为如果判断某个对象的类型的话,用isinstance会根据树的形状去搜索,从叶子搜索到跟就可以判断是否是相同类型,#就算是不同对象可能是相同类型,然而type是同种类型,但不同对象。
复制代码
1.4、类变量和实例变量
  1. class A:    a = 1 #a是类变量    def __init__(self,x,y):#self是类的实例 x与y已经绑定到实例上的属性上了        self.x = x        self.y = ynum = A(2,3)# A.a = 11   #如果修改类属性,那么实例的值也会跟着变# num.a = 100  #如果修改实例属性,那么类属性的值不变,# 会在对象中新建一个实例属性的值,寻找的时候直接对象属性中寻找。print(num.x,num.y,num.a) #2 3 1  为什么实例num能够找到A的类属性呢,# 首先实例num先在实例属性种寻找,如果没有找到的话就会向上寻找,找到类属性print(A.a) # 1 类属性print(A.x)#AttributeError: type object 'A' has no attribute 'x'#类找实例属性找不到是因为类首先到自己的属性中找,如果没有找到的话,就不会向下寻找
复制代码
1.5、类和实例属性的查找顺序----mro查找
类查找属性的查找顺序有深度优先查找广度优先查找
广度优先查找:
  1. #python3以后称为新式类,全部都继承objectclass D:    passclass C(D):    passclass B(D):    passclass A(B,C):    passprint(A.__mro__) #(, , , , )#__mro__魔法方法直接显示出类查找属性的顺序
复制代码


深度优先查找:
  1. #python3以后称为新式类,全部都继承objectclass E:    passclass D:    passclass C(E):    passclass B(D):    passclass A(B,C):    passprint(A.__mro__)#(, , , , , )
复制代码


  但在python3中为了避免深度优先算法与广度优先算法混乱,出现了C3算法避免了两种算法出现的问题,例如菱形搜索应用深度优先算法,从AB再到D找到方法,可能C中重写了D的方法,因此深度优先算法不能解决菱形搜索的情况,然而C3算法解决了以上出现的两种情况。
1.6、类方法、静态方法和实例方法
[code]class Date:    def __init__(self,year,month,day):        self.year = year        self.month = month        self.day = day    #静态方法的缺点就是硬编码,如果换类名又要重新改返回的类名    @staticmethod    def parse_from_string(date_str):        year,month,day = tuple(date_str.split("-"))        return Date(int(year),int(month),int(day))    #为啥不用classmethod替换staticmethod呢?    #检查时间格式是否正确,不需要对象返回回来,因此这个时候它就有用了,而其余都是要将对象返回回来    @staticmethod    def valid_str(date_str):        year, month, day = tuple(date_str.split("-"))        if int(year)>0 and (int(month)>0 and int(month)
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|网站地图|java自学教程|www.konglongmei.com

GMT+8, 2020-7-13 09:06 , Processed in 0.345610 second(s), 47 queries .

快速回复 返回顶部 返回列表