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

Posted 简明现代魔法

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了13.面向对象(多态/(性)/封装)相关的知识,希望对你有一定的参考价值。

多态

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

封装

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

第一种封装:
什么都不做

第二种封装:
双下划线的隐藏属性
语法糖:__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.面向对象(多态/(性)/封装)的主要内容,如果未能解决你的问题,请参考以下文章

面向对象的多态详解 !!

面向对象的封装性和多态性

13.面向对象一

JAVA- 面向对象的三大特征(封装性继承性多态性)

Python3-2020-测试开发-20- 面向对象之封装,继承,多态

面向对象的特征