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.面向对象(多态/(性)/封装)的主要内容,如果未能解决你的问题,请参考以下文章