封装的两个层面:
1类和对象的名称空间,本质就是一种封装 类名. 和 实例名. 就是访问隐藏属性的接口
2. 类中把某些属性和方法隐藏起来,只在内部使用,外部无法访问,或留下少量接口(函数)供外部访问
只有在类定义时自动变形: __x =======> _类名__x
定义后的赋值操作不会变形,不会隐藏
类的里面可以直接访问变形的属性名
对于这一层面的封装,我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后在外部就可以使用了
这种机制并没有真正意义上限制我们访问隐藏的属性,只要知道类名和变量名就可以访问
#封装
class A:
__x = 1 #_A__x = 1
def __test(self): #_A__test(self)
print('from A')
print(A._A__x)
A._A__test(11111)
print(A.__dict__)
a = A()
print(a._A__x)
a._A__test()
print(a.__dict__)
#__名字,这种语法,只在定义的时候才会有变形的效果,如果类或对象已经产生,就不会有变形效果
class B:
pass
B.__x = 1
print(B.__dict__)
b = B()
b.__x = 1
print(b.__dict__)
class A:
def __init__(self):
self.__x = 1
def tell(self):
print(self.__x) #在类内部可以直接使用__名字在访问变形的属性名
a = A()
print(a.__dict__)
#print(a.__x) #报错
a.tell()
#在定义的阶段就会变形
class A:
def __fa(self): #_A__fa()
print('from A')
def test(self): #类内部定义一个函数作为接口,外部可以访问类的私有属性
self.__fa() #_A__fa()
class B(A):
def __fa(self): #_B__fa()
print('from B')
b = B()
b.test()
View Code
# 将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,
# 这种特性的使用方式遵循了统一访问的原则
#@property的第一个用法:把函数属性伪装成数据属性,方便使用者调用
import math
class Circle:
def __init__(self,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(7)
print(c.radius)
c.radius = 10
print(c.area)
print(c.perimeter)
class Room:
def __init__(self,length,weith,height):
self.length = length
self.weith = weith
self.height = height
@property
def area(self):
return self.length*self.weith
@property
def volume(self):
return self.length*self.weith*self.height
r = Room(1,3,5)
print(r.length)
print(r.area)
print(r.volume)
View Code
#被property装饰的属性会优先于对象的属性被使用
#被property装饰的属性,如下面的sex,分成3种:
# 1 property 查看
# 2 sex.setter 修改
# 3 sex.deleter 删除
#@property的第二个用法:自定义对象对于所属类的属性的访问
class People:
def __init__(self,name,SEX):
self.name = name
self.sex = SEX
@property #使用装饰器将之变为数据属性
def sex(self): #定义接口函数,使外部可以访问私有变量
print('-----------')
return self.__sex
@sex.setter
def sex(self,value):
if not isinstance(value,str):
raise TypeError('性别必须是字符串')
self.__sex = value
@sex.deleter
def sex(self):
del self.__sex
p = People('egon','male')
print(p.sex)
p.sex = 'famale'
print(p.sex)
del p.sex
print(p.sex)
View Code
#静态方法与类方法 都是为类量身定制的,一个是类的另一种实例化对象的方法,一个是他的子类的另一种实例化对象的方法
staticmethod # 解除绑定: 类的另一种创建对象的方法
# 应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时静态方法就派上用场了
class Foo:
@staticmethod
def spam(self):
print('-------->',self)
Foo.spam(22222)
f = Foo()
f.spam(222)
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()
obj = Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并返回
return obj
@staticmethod
def tomorrow():
t = time.localtime(time.time()+86400)
obj = Date(t.tm_year,t.tm_mon,t.tm_mday)
return obj
# 但凡是定义在类的内部,并且被staticmethod装饰器修饰过的方法,都是解除绑定的方法,实际上就是函数,没有自动传值功能
d1 = Date(2017,5,6)
d2 = Date(2017,6,9)
date_now = Date.now() #类可以调用自己的函数 进行实例化对象
print(date_now)
print(d1.now)
d_n1=d1.now() #对象也可以调用类的函数 进行实例化对象,但仍然是类实例化出来的,尽量不要用
# 但凡是定义在类的内部,并且没有被任何装饰器修饰过的方法,都是绑定方法,有自动传值功能
d_n1 = Date.now()
print(d_n1.year)
d_n1.now() #绑定方法会自动传值,但是类的函数并不需要参数
View Code
classmethod #给类绑定方法 :类和它的子类的另一种实例化对象的方法
class Foo:
def bar(self):
pass
@classmethod #把一个方法绑定给类:类.方法 会把类本身当做第一个参数自动传给绑定方法
def test(cls,x):
print(cls,x)
cls() #拿到一个类的内存地址后,就可以实例化或者应用类的属性了
print(Foo.bar) #函数
print(Foo.test) #Foo 的绑定方法
Foo.test(1)
f = Foo()
print(f.test)
f.test(4)
View Code
# __str__定义在类内部,必须返回一个字符串类型
# 什么时候会触发它的执行:打印由这个类产生的对象时,会触发执行
class People:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self): #自定义类的str函数
return '<name:%s,age:%s>'%(self.name,self.age)
p1 = People('egon',18)
print(p1)
print(str(p1))
View Code
#应用场景
import time
class Date:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
@classmethod #类的绑定方法
def now(cls):
t = time.localtime()
obj = cls(t.tm_year,t.tm_mon,t.tm_mday)
return obj
class EuroDate(Date):
def __str__(self): #打印它的对象时触发
return '年:%s,月:%s,日:%s'%(self.year,self.month,self.day)
# e = EuroDate(1,1,1)
# print(e)
e1 = EuroDate.now()
print(e1)
View Code