欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python学习日记(二十六) 封装和几个装饰器函数

程序员文章站 2022-03-25 22:25:20
封装 广义上的封装,它其实是一种面向对象的思想,它能够保护代码;狭义上的封装是面向对象三大特性之一,能把属性和方法都藏起来不让人看见 私有属性 私有属性表示方式即在一个属性名前加上两个双下划线 python的私有属性只是在代码的级别给加了一层密,不允许你直接去访问这些私有属性,像你去打印一个私有属性 ......

封装

广义上的封装,它其实是一种面向对象的思想,它能够保护代码;狭义上的封装是面向对象三大特性之一,能把属性和方法都藏起来不让人看见

私有属性

私有属性表示方式即在一个属性名前加上两个双下划线

class fighter:
    def __init__(self,name,hp,atk,speed):
        self.name = name
        self.__hp = hp                      #私有属性  变形成self._fighter__hp
        self.__atk = atk                    #私有属性  变形为self._fighter__atk
        self.__speed = speed                #私有属性  变形为self._fighter__speed
f1 = fighter('akk-18',1500,200,300)
print(f1.__dict__)                          #{'name': 'akk-18', '_fighter__hp': 1500, '_fighter__atk': 200, '_fighter__speed': 300}

python的私有属性只是在代码的级别给加了一层密,不允许你直接去访问这些私有属性,像你去打印一个私有属性它的结果就是报错,或者你想尝试修改一个私有属性的时候,结果并不能修改成功

f1.__hp = 1000
print(f1.__dict__)                          #{'name': 'akk-18', '_fighter__hp': 1500, '_fighter__atk': 200, '_fighter__speed': 300, '__hp': 1000}

因为上面的私有属性都已经变形成_类名__属性名的格式,我们这样写,相当于直接给self添加了一个新的'键名'__hp,所以在类的外部调用双下划线就不行

那如何访问我们的私有属性呢?我们可以看到上面的私有属性都被变形成了_类名__属性名的形式,那么我们可以这样去访问:

print(f1._fighter__hp)                      #1500
print(f1._fighter__atk)                     #200
print(f1._fighter__speed)                   #300

但光这样走这样的捷径是不完美的,我们可以在类内部添加一个get_value函数来获得那些私有属性

class fighter:
    def __init__(self,name,hp,atk,speed):
        self.name = name
        self.__hp = hp                      #私有属性
        self.__atk = atk                    #私有属性
        self.__speed = speed                #私有属性
    def get_value(self):
        return self.__hp,self.__atk,self.__speed        #只要在类内部使用私有属性就会自动带上'_类名’
f1 = fighter('akk-18',1500,200,300)
print(f1.__dict__)                          #{'name': 'akk-18', '_fighter__hp': 1500, '_fighter__atk': 200, '_fighter__speed': 300}
print(f1.get_value())                       #(1500, 200, 300)
for i in f1.get_value():
    print(i)                                #1500
                                            #200
                                            #300

在get_value中return的写法其实就是变形后再获取私有属性

def get_value(self):
    return self._fighter__hp,self._fighter__atk,self._fighter__speed

私有静态属性

class fighter:
    __id = 1234445                          #私有静态属性
    def __init__(self,name,hp,atk,speed):
        self.name = name
        self.__hp = hp                      #私有属性
        self.__atk = atk                    #私有属性
        self.__speed = speed                #私有属性
    def get_value(self):                    #获取内部私有属性方法
        return self.__hp,self.__atk,self.__speed
    def get_id(self):
        return self.__id
f1 = fighter('akk-18',1500,200,300)
print(f1._fighter__id)                      #1234445
print(f1.get_id())                          #1234445

私有方法

class fighter:
    __id = 1234445                          #私有静态属性
    def __init__(self,name,hp,atk,speed):
        self.name = name
        self.__hp = hp                      #私有属性
        self.__atk = atk                    #私有属性
        self.__speed = speed                #私有属性
    def get_value(self):                    #获取内部私有属性方法
        return self.__hp,self.__atk,self.__speed
    def get_id(self):                       #获取静态属性id的方法
        return self.__id
    def __shoot(self):                      #私有方法
        print('shooting!')
    def get_shoot(self):                    #获取私有方法shoot
        self.__shoot()                      
        #self._fighter__shoot()
f1 = fighter('akk-18',1500,200,300)
f1.get_shoot()                              #shooting!

修改私有属性

class room:
    def __init__(self,name,length,width):
        self.__name = name
        self.__length = length
        self.__width = width
    def area(self):
        return self.__width*self.__length
    def get_name(self):         #获取这个私有名字的方法
        return self.__name
    def set_name(self,new_name):#重新设定这个房号
        if type(new_name) is str and new_name.isdigit() == true:
            self.__name = new_name
        else:
            print('错误的命名方式!')
r1 = room('1101',15,20)
print(r1.area())                #300
print(r1.get_name())            #1101
r1.set_name('asd')
print(r1.__dict__)              #{'_room__name': '1103', '_room__length': 15, '_room__width': 20}
print(r1.get_name())            #1103

 

几个装饰器

1.@property

我们在用面向对象的思想去写一个圆经常会把它的周长和面积当成一个方法,然而我们知道方法是一个获取的动作,而不是一个属性名词,所以要想处理这种将一个方法伪装成为一个属性就要用到@property,这样这个方法它就看起来像是一个属性了

from math import pi as p
class circle:
    def __init__(self,r):
        self.r = r
    def perimeter(self):
        return 2*p*self.r
    def area(self):
        return p*self.r**2
c = circle(5)
print(c.area())                     #78.53981633974483
print(c.perimeter())                #31.41592653589793

改进:

from math import pi as p
class circle:
    def __init__(self,r):
        self.r = r
    @property
    def perimeter(self):        #这里在self的后面不能传任何参数
        return 2*p*self.r
    @property
    def area(self):             #这里在self的后面不能传任何参数
        return p*self.r**2
c = circle(5)
print(c.perimeter)              #31.41592653589793
print(c.area)                   #78.53981633974483

bmi问题:bmi指数(bmi)=体重(kg)÷身高^2(m)

    成人的bmi数值:

    过轻:低于18.5

    正常:18.5-23.9

    过重:24-27

    非常肥胖:>32

class bmi:
    def __init__(self,height,weight):
        self.__height = height
        self.__weight = weight
    @property
    def __bmi(self):                        #把计算bmi的方法伪装成一个属性
        return self.__weight/self.__height**2

    def get_bmi(self):                      #bmi判断
        if self.__bmi > 32:
            print('非常肥胖!')
        elif 24 <= self.__bmi <= 27:
            print('过重!')
        elif 18.5 <= self.__bmi <= 23.9:
            print('正常!')
        else:
            print('过轻!')

b = bmi(1.75,72)
b.get_bmi()                                 #正常!
在@property的前提下用@setter来修改属性
class person:
    def __init__(self,name,):
        self.__name = name
    @property
    def name(self):
        return self.__name + ' hello!'
    @name.setter
    def name(self,new_name):
        self.__name = new_name
p1 = person('jogn')
print(p1.name)                               #jogn hello!
#p1._person__name = 'maria'                  #maria hello!
print(p1.name)                               #maria hello!
在@property的前提下用@deleter来删除属性
class person:
    def __init__(self,name,):
        self.__name = name
    @property
    def name(self):
        return self.__name + ' hello!'
    @name.deleter
    def name(self):                     
        del self.name                       
p1 = person('jogn')
print(p1.name)                               #jogn hello!
del p1.name                                  #del p1.name它在这里的作用是指向deleter下面这个方法,再去做这个方法的功能
print(p1.name)                               #找不到这个名字了

我们可以看一下在这里的del关键字它起到了什么的作用

class person:
    def __init__(self,name,):
        self.__name = name
    @property
    def name(self):
        return self.__name + ' hello!'
    @name.deleter
    def name(self):
        print('执行了这个方法!')
p1 = person('jogn')
print(p1.name)                               #jogn hello!
del p1.name                                  #del p1.name它在这里的作用是指向deleter下面这个方法,再去做这个方法的功能
print(p1.name)                               #jogn hello!

因为第二个name里面没有关于修改名字的操作所以在最后p1.name还能打印出原来的名字

2.@classmethod

当这个方法只涉及到静态属性的时候,就应该使用classmethod来装饰这个方法

class goods:
    __discount = 0.8                                    #折扣
    def __init__(self,name,price):
        self.name = name
        self.__price = price
    @property
    def price(self):
        return self.__price * self.__discount
    @classmethod
    def change_discount(cls,new_discount):              #cls为类名
        cls.__discount = new_discount                   #修改折扣
apple = goods('苹果',4)
print(apple.price)                                      #3.2
goods.change_discount(0.5)
print(apple.price)                                      #2.0

3.@staticmethod

在完全面向对象的程序中,如果一个函数和对象没有关系那么就用staticmethod把这个函数变成一个静态方法

class login:
    def __init__(self,name,password):
        self.name = name
        self.password = password
    @staticmethod
    def get_n_psw():                            #没有默认参数就像函数一样
        username = input('请输入用户名:')
        psw = input('请输入密码:')
        login(username,psw)                     #请输入用户名:asdd                                               
        return username,psw                     #请输入密码:4564648
print(login.get_n_psw())                        #('asdd', '4564648')