美文网首页
13. 面向对象(多态/封装)

13. 面向对象(多态/封装)

作者: 派大星的喜悲没人看见 | 来源:发表于2017-10-23 12:26 被阅读9次

    [TOC]

    多态

    同一种事物的多种形态
    增加了程序的灵活性
    增加了程序的可扩展性

    封装

    封装数据:保护隐私
    封装方法:隔离复杂度

    第一种封装:
    什么都不做

    第二种封装:
    双下划线的隐藏属性
    语法糖:_xxx====>类__xxx #这个过程就是变形

    特性

    @property #xxx = property(xxx)
    def xxx():
    pass

    class Squera:      #定义一个正方形的类
        def __init__(self,lenth):   #定义一个边长参数
            self.lenth = lenth
    
        @property     #使用特性装饰器,不用调用外部模块,因为可以在内置模块中看到property()函数,这个装饰器的实现效果其实就是area = property(area)
        def area(self):
            return self.lenth*self.lenth
    
        @property
        def perimeter(self):
            return 4* self.lenth
    
    s = Squera(10)
    # print(s.area())    #如果不添加property,打印这个面积需要使用调用函数的方法,但是实际上,为了统一这些参数的调用,并且规范调用方式,所以才使用property
    # print(s.perimeter())
    
    print(s.area)
    print(s.perimeter)
    

    除了如上注释中描述的优点,我们需要注意,这个area和perimeter看起来像作为一个参数属性在引用,但是实际上他本质上仍然是一个计算函数的结果。
    所以在修改了lenth的长度之后,再次调用print(s.area)就会发现结果已经改变了。

    被property装饰的属性会优于对象的属性被引用。并且property的相关方法也会关联一些语句,并且优先走有关联语句的部分,例如下面代码的xxx.name = 'xxxxx'就指向了name.setter函数,无论这个赋值是多少,优先运行name.setter的函数内容。
    如果说这个name.setter中并没有赋值相关的操作,那么这个动作就按照name.setter来运行。不执行赋值。

    class Name:
        def __init__(self,NAME):
            self.__name = NAME
    
        @property   #将一个隐藏属性定义成一个函数,并且把这个函数的计算结果返回出来
        def name(self):
            print(self.__name)
    
        @name.setter  #setter接口,用于设置之前property装饰的函数,在函数内部修改被隐藏的值
        def name(self,value):  #定义一个可用值传入
            if type(value)==str:
                self.__name = value   #将隐藏值在函数内部修改,如果在外部直接修改name,因为name是被property装饰的一个函数,所以无法修改,起到了保护原隐藏值的作用。
            else:
                raise TypeError('please enter str type')
    
        @name.deleter
        def name(self):   #定义删除原隐藏值函数
            del self.__name
    
    peo1 = Name('scott')
    #定义对象peo1
    print(peo1.name)
    #输出对象名,由于__name 被隐藏,所以这里输出的其实是name()的运算结果
    peo1.name = 'jerry'
    #当出现针对name(其实是函数)的赋值动作,被setter装饰的函数运行;扩展:可以进行添加age隐藏参数,同__name,也给age添加修改方法,但是添加name.setter,看修改的是哪个值。需要注意setter方法前需要注明修改的对象
    print(peo1.name)#查看结果
    peo1.name = 123     #输入错误类
    print(peo1.name)
    del peo1.name    #出现删除操作的时候,执行.deleter装饰的方法
    print(peo1.name)
    
    

    str

    class people:
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def __str__(self):
            return 'name:%s,age:%s'%(self.name,self.age)
    
    p1 = people('scott',23)
    print(p1)
    
    >>>name:'scott',age:23
    

    定义在类的内部,必须返回一个字符串类型。
    打印有这个类产生的对象时,会触发执行。
    其实str(p1)=========>p1.str()

    staticmethod解除绑定方法

    添加了@staticmethod之后,类中的方法就不再是绑定方法了,也就不存在self自动传值的动作。
    在类中,需要定义一个函数,就是给类使用的,而不和对象绑定,就要用到staticmethod。如果需要调用一个类的方法,并且不传入self(也就是不绑定对象),也没有使用staticmethod,那么在实例调用的时候,就会报缺少参数的错。

    import time
    class Date:
        def __init__(self,year,month,day):# 定义一个基础函数,传入年月日
            self.year = year
            self.month = month
            self.day = day
    
        def test():            #定义一个test()方法,这个方法不与类绑定,在调用的时候与对象传参无关(不接受对象的传参)这时候其实test会有红线提示,不规范的写法
            print('from test') #返回一个标识结果
    
        @staticmethod          #静态方法(用于将装饰的函数从类中解除绑定)
        def now():             #定义的函数无需self传值,但仍然可以作为类中的方法被对象调用
            t = time.localtime()    #.localtime()地方法传给t,是一个类,包含着年月日时间参数
            obj = Date(t.tm_year,t.tm_mon,t.tm_mday) #将t的年月日信息传给Date类,生成obj对象
            return obj     #返回,将now()的结果变成obj对象
    
        @staticmethod
        def tomorrow():
            t = time.localtime(time.time()+86400)
            return Date(t.tm_year,t.tm_mon,t.tm_mday)
    
    class EuroDate(Date):
        def __str__(self):
            return '%s year %s month %s day'%((self.year,self.month,self.day))
    
    d = Date(17,4,20)     #设置对象
    d.test()              #报错,对象不能调用test方法
    #TypeError: test() takes 0 positional arguments but 1 was given
    Date.test()           #类可以调用方法,但是没有意义,此时的test和类外部的函数一样。
    
    

    静态方法的引用:

    t = d.now()
    print(t.year,t.month,t.day)
    l = time.localtime()
    print(type(l))
    

    结果

    2017 4 21
    <class 'time.struct_time'>
    

    实例也可以使用,但通常静态方法都是给类用的,实例在使用时丧失了自动传值的机制

    在类中,如果没被装饰器装饰过,就是绑定方法

    import time
    class Date:
        def __init__(self,year,month,day):
            self.year=year
            self.month=month
            self.day=day
        @staticmethod
        def now():
            t=time.localtime()
            return Date(t.tm_year,t.tm_mon,t.tm_mday)
    
    class EuroDate(Date):
        def __str__(self):
            return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)
    
    e=EuroDate.now()
    print(e) #我们的意图是想触发EuroDate.__str__,但是结果为
    '''
    输出结果:
    <__main__.Date object at 0x1013f9d68>
    '''
    

    classmethod

    @classmethod
    http://blog.csdn.net/handsomekang/article/details/9615239#

    把一个方法绑定给类

    一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。

    而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。

    这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。

    既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢

    从它们的使用上来看,

    • @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
    • @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。

    如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。

    而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

    差别:绑定给对象的,第一个位置不用传参staticmethod
    绑定给类的(类.绑定给类的方法()),会把类本身当作第一个参数(原self)传给类

    class A(object):
        bar = 1
        def foo(self):
            print 'foo'
    
        @staticmethod
    def static_foo():
        print 'static_foo'
        print A.bar
    
    @classmethod
    def class_foo(cls):
        print 'class_foo'
        print cls.bar
        cls().foo()
    
    A.static_foo()
    A.class_foo() 
    

    拿到一个类的内存地址后,就可以实例化或者引用类的属性了。

    小结:

    在类内部定义的函数无非三种用途
    一:绑定到对象的方法
        只要是在类内部定义的,并且没有被任何装饰器修饰过的方法,都是绑定到对象的
    
        class Foo:
            def test(self): #绑定到对象的方法
                pass
            def test1(): #也是绑定到对象的方法,只是对象.test1(),会把对象本身自动传给test1,因test1没有参数所以会抛出异常
                pass
    
        绑定到对象,指的是:就给对象去用,
        使用方式:对象.对象的绑定方法(),不用为self传值
        特性:调用时会把对象本身当做第一个参数传给对象的绑定方法
    
    二:绑定到类的方法:classmethod
        在类内部定义的,并且被装饰器@classmethod修饰过的方法,都是绑定到类的
    
    #用来计算类被实例化的次数
    # def get_no_(cls_obj):
    #     return cls_obj.times_inst
    
    class Exm_cls:
       #实例化次数的初始值为0
       times_inst = 0
       #类被实例化一次,就+1
       def __init__(self):
           Exm_cls.times_inst +=1
    
       #在内部定义这个函数,并且把他绑定到类
       @classmethod
       def get_no_(cls):
           return cls.times_inst
    
    
    exm1 = Exm_cls()
    exm2 = Exm_cls()
    
    print(Exm_cls.get_no_())
    # print(get_no_(Exm_cls))    
        绑定到对象,指的是:就给对象去用,
        使用方式:对象.对象的绑定方法()
        特性:调用时会把对象本身当做第一个参数传给对象的绑定方法
    
    三:解除绑定的方法:staticmethod
        既不与类绑定,也不与对象绑定,不与任何事物绑定
        绑定的特性:自动传值(绑定到类的就是自动传类,绑定到对象的就自动传对象)
        解除绑定的特性:不管是类还是对象来调用,都没有自动传值这么一说了
    
        所以说staticmethod就是相当于一个普通的工具包
    
    class Foo:
        def test1(self):
            pass
        def test2():
            pass
    
        @classmethod
        def test3(cls):
            pass
        @classmethod
        def test4():
            pass
    
        @staticmethod
        def test5():
            pass
    
    test1与test2都是绑定到对象方法:调用时就是操作对象本身
        <function Foo.test1 at 0x0000000000D8E488>
        <function Foo.test2 at 0x0000000000D8E510>
    test3与test4都是绑定到类的方法:调用时就是操作类本身
        <bound method Foo.test3 of <class '__main__.Foo'>>
        <bound method Foo.test4 of <class '__main__.Foo'>>
    test5是不与任何事物绑定的:就是一个工具包,谁来都可以用,没说专门操作谁这么一说
        <function Foo.test5 at 0x0000000000D8E6A8>
    

    反射
    getattr
    setattr
    delattr
    hasattr

    定制

    相关文章

      网友评论

          本文标题:13. 面向对象(多态/封装)

          本文链接:https://www.haomeiwen.com/subject/axzouxtx.html