文章目录
1.封装的引入
-
封装是面向对象的三大特性之一
- 封装就是指隐藏对象中一些不希望被外部访问的属性和方法,仅对外提供公共方式
- 将对象的属性名修改为一个外部不知道的名字
- 如果要修改属性,我们要提供一个getter和setter方法可以访问到属性并修改
-
使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全
• 1. 隐藏属性名,使调用者无法随意的修改对象中的属性
• 2. 增加了getter()和setter()方法,很好控制属性是否是只读的
• 3. 使用setter()设置属性,可以增加数据的验证,确保数据是正确的
• 4. 使用getter()方法获取属性,使用setter()方法设置属性可以在读取属性和修改属性的同时做一些其他的处理
2.特殊方法
class Person:
# name = '葫芦娃'
# 在类中可以定义一些特殊的方法,也称魔术方法,形如__xxx__()
# 这些特殊方法不需要我们自己调用,特殊方法会在特殊时候自动调用
print('类中的代码块') # 无论他放在哪个位置,总是先执行,再执行__init__
def __init__(self,name):
self.name = name
# print('hello')
# print('类中的代码块') # 无论他放在哪个位置,总是先执行,再执行__init__
def speak(self):
print('大家好,我是%s'%self.name)
p1= Person('钢铁侠') # 在实例化时就自动执行__init__,但是先执行类中代码块 : print('类中的代码块')
p2 = Person('黑夜')
p3 = Person('黑寡妇')
p1.speak()
p2.speak()
p3.speak()
3.私有变量和私有方法
3.1私有变量
#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
class A:
__N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
def __init__(self):
self.__X=10 #变形为self._A__X
def __foo(self): #变形为_A__foo
print('from A')
def bar(self):
self.__foo() #只有在类内部才可以通过__foo的形式访问到.
#a._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形
3.2 私有方法
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
class A:
def fa(self):
print('from A')
def test(self):
self.fa()
class B(A):
def fa(self):
print('from B')
b = B()
b.test() # from B
#把fa定义成私有的,即__fa
class A:
def __fa(self): #在定义时就变形为_A__fa
print('from A')
def test(self):
self.__fa()
class B(A):
def __fa(self): #只会与自己所在的类为准,即调用_A__fa
print('from B')
b = B()
b.test() # from A
4. 封装与扩展
4.1类的设计者与使用者
- 封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
#类的设计者
class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high
def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
return self.__width * self.__length
#使用者
>>> r1=Room('卧室','egon',20,20,20)
>>> r1.tell_area() #使用者调用接口tell_area
#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high
def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
return self.__width * self.__length * self.__high
#对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
>>> r1.tell_area()
4.2 property属性
- property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
import math
class Circle:
def __init__(self,radius): #圆的半径radius
self.radius=radius
@property
def area(self):
return math.pi * self.radius**2 #计算面积
@property
def perimeter(self):
return 2*math.pi*self.radius #计算周长
@area.setter
def area(self, new_radius):
self.radius = new_radius
c=Circle(10)
print(c.radius) # 10
print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
# 314.1592653589793
print(c.perimeter) #同上 # 62.83185307179586
c.area=1
print(c.area) # 3.141592653589793
'''
但是注意,这里不能修改area和perimeter的属性值
如c.area=3
会抛出异常 AttributeError: can't set attribute
'''
# 不过可以使用这样进行修改 @属性名.setter定义一个函数,见上
- 一个静态属性property本质就是实现了get,set,delete三种方法
class Foo:
@property
def AAA(self):
print('get的时候运行我啊')
@AAA.setter
def AAA(self,value):
print('set的时候运行我啊')
@AAA.deleter
def AAA(self):
print('delete的时候运行我啊')
'''只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter'''
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
等价
class Foo:
def get_AAA(self):
print('get的时候运行我啊')
def set_AAA(self,value):
print('set的时候运行我啊')
def delete_AAA(self):
print('delete的时候运行我啊')
AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
4.3classmethod & staticmethod
class A:
__role = 'CHINA'
@classmethod
def show_role(cls):
print(cls.__role)
@staticmethod
def get_role():
return A.__role
@property
def role(self):
return self.__role
a = A()
print(a.role)
print(a.get_role())
a.show_role()
# __role在类中有哪些身份?
# 以上代码分别输出哪些内容?
# 这三个装饰器分别起了什么作用?有哪些区别?
相同点:在python中,静态方法和类方法都是可以通过类对象和类对象实例访问。
区别:
- @classmethod 是一个函数修饰符,它表示接下来的是一个类方法,而对于平常我们见到的则叫做实例方法。 类方法的第一个参数cls,而实例方法的第一个参数是self,表示该类的一个实例。
- 普通对象方法至少需要一个self参数,代表类对象实例
- 类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。 对于类方法,可以通过类来调用,就像C.f(),有点类似C++中的静态方法, 也可以通过类的一个实例来调用,就像C().f(),这里C(),写成这样之后它就是类的一个实例了。
- 静态方法则没有,它基本上跟一个全局函数相同,一般来说用的很少
class A(object):
@classmethod
def cm(cls):
print '类方法cm(cls)调用者:', cls.__name__
@staticmethod
def sm():
print '静态方法sm()被调用'
class B(A):
pass
A.cm() # A
B.cm() # B
A.sm() # 静态方法sm()被调用
B.sm() # 静态方法sm()被调用