封装&特殊方法

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()被调用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值