Python 基础第二十二天(封装)

今日主要内容:

1.封装

2.内置函数

property

classmethod

staticmethod

 

封装

什么是封装:

在python中就是将类里面的静态变量,属性,方法进行私有化处理,只能类内进行访问,类外无法继承,调用.

例1:对静态变量进行封装,静态变量的私有化

class A:
    __n = 'aaa'
print(a.__n) #错误,静态变量被封装后,不能类外调用

class A:
    __n = 'aaa'
    def func(self):
        print(A.__n)  #正确

 

python中封装/私有化的格式:

python就是把__名字当成私有的语法,__为两个下划线

那么,python中类是否定义了私有化呢?

print(A.__dict__)
结果:
{'__module__': '__main__', '_A__N': 'aaa', 'func': <function A.func at 0x0000000001E6B8C8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

说明:

在类定义了私有化后,python解释器就对__N进行了名称的转换然后存储在内存中,所以在外部如果还用定义的名称定义的话就会报错:

解释器给转换成了:_类名__名字

总结:

一个私有的名字 在存储的过程中仍然会出现在A.__dict__中,所以我们仍然可以调用到。
python对其的名字进行了修改: _类名__名字
只不过在类的外部调用 :需要“_类名__名字”去使用
在类的内部可以正常的使用名字

在类内 只要你的代码遇到__名字,就会被python解释器自动的转换成_类名__名字

例2:属性的私有化

class B:
    def __init__(self,name):
        self._name = name
    def func(self):
        print('in func:%s' %self._name)
b = B('alex')
b.func()

例3:私有化的方法:

class C:
    def __wahaha(self):
        print('wahaha')

c = C()
c.__wahaha()  #报错:AttributeError: 'C' object has no attribute '__wahaha'


class C:
    def _wahaha(self):
        print('wahaha')
    def ADCa(self):
        self.wahaha()

c = C()
c.ADCa()  #这样就可以访问了

总结:

在类中,静态属性,方法,对象属性都可以变成私有的,只需要在这些名字之前加上__

面试题:

例:考察私有方法继承的问题:

class D:
    def __init__(self):
        self.__func()
    def __func(self):
        print('in D')

class E(D):
    def __func(self):
        print('in E')
e = E()


结果:in D

私有的名字,在类内使用的时候,就是会变形成_该类名__方法名
以此为例 :没有双下换线会先找E中的func
但是有了双下划线,会在调用这个名字的类D中直接找_D__func

过程如图:

解析:

1.内存中创建内存空间 D,E  2.对E进行实例化,内存中添加 e对象的内存空间

3.实例化的过程中会查找__init__过程,E内没有,所以找到E的父类D,

4.__init__中定义的__func属性,转换成 _D__func()

5.执行这个私有方法,所以打印'in D'

 

Java和python关于类内方法的比较:

java中的对比
public 公有的 在类的内部可以使用,子类可以使用,外部可以使用.    python中所有正常的名字
protect 保护的 在类的内部可以使用,子类可以使用,外部不可以使用.       python中没有
private 私有的 只能在类的内部使用,子类和外部都不可以使用

python中私有的用法:

1.当一个方法不想被子类继承的时候

2.有些属性或者方法不希望从外部被调用,只想提供给内部的方法使用

 

内置函数

property 

作用:

将类内的一个方法伪装成一个属性

这样做的好处:

1.并不会让你的代码有什么逻辑上的提高
2.只是从调用者的角度上换了一种方式,使之看起来更合理

例:

#没有使用property
from math import pi
class Circle:
    def __init__(self,r):
        self.r =r
    def cal_area(self):
        return self.r ** 2 * pi
    def cal_perimeter(self):
        return 2*pi* self.r
c = Circle(6)
print(c.cal_area())  #类.方法()
print(c.cal_perimeter())

#使用property方法对代码进行改进或伪装
from math import pi
class Circle:
    def __init__(self,r):
        self.r = r
    @property
    def area(self):
      return self.r**2*pi    
    @property
    def perimeter(self):
       return 2*pi*self.r
 
c = Circle(6)
print(c.area)   #变成 类.属性名
print(c.perimeter)
                                 

结论:

被property装饰的area,perimeter仍然是一个方法 存在Circle.__dict__(存在于类.__dict__中)
对象的.__dict__中不会存储这个属性

在一个类加载的过程中,会先加载这个中的名字,包括被property装饰的
在实例化对象的时候,python解释器会先到类的空间里看看有没有这个被装饰的属性,
如果有就不能再在自己对象的空间中创建这个属性了

 

property方法的修改和删除

property的修改: @方法名.setter  (之前@property装饰的方法名一致,两个装饰方法修饰的方法名称要一致.)

class Person:
    def __init__(self,n):
        self.__name = n   #私有属性
    @property
    def name(self):
        return self.__name
    @name.setter       #对定义的name 方法进行修改,两个方法名称可以一致,@name == @property装饰的名字
    def name(name,new_name):
        if type(new_name) is str:  #判断填入类型
               self.__name = new_name
        else:
                print('填入的数据类型错误')

p = Person('alex')
print(p.name)
p.name = 'jason' #操作的是@name.setter下面装饰的方法
print('p.name')

property的删除:@方法名.deleter (之前@property装饰的方法名一致,两个装饰方法修饰的方法名称要一致.)

例:

class Person:
    def __init__(self,n):
        self.__nn = n   #私有属性
    @property
    def name(self):
        return self.__nn
    @name.deleter
    def name(self):
        del self.__nn
p = Person('alex')
print(p.name)
del p.name     #删除@name.deleter下面装饰的方法
print(p.name)

#结论:
#AttributeError: 'Person' object has no attribute '_Person__nn'

总结:

@property --> func     将方法伪装成属性,只是用来好看的

@func.setter --> func  对伪装的属性进行赋值的时候调用这个方法 一般情况下用来做修改

@func.deleter --> func 在执行del 对象.func的时候调用这个方法 一般情况下用来做删除 基本不用

 

作用

将一些需要随着一部分属性的变化而变化的值的计算过程 从方法 伪装成属性
将私有的属性保护起来,让修改的部分增加一些约束,来提高程序的稳定性和数据的安全性

 

classmethod

作用:

类方法 可以直接被类调用 不需要默认传对象参数 只需要传一个类参数就可以了

 例:

class Goods:
    __discount = 0.8
    def __init__(self,name,price):
        self.name = name
        self._price = price
    @property
    def or_price(self):
        return self.__price * Goods.__discount

    @classmethod
    def ch_discount(cls,new_discount)  #cls表示类方法参数,调用时需要类
        cls.__discount  = new_discount

Goods.ch_discount(1) #类函数classmethod的引用,直接填入参数'new_discount'
apple = Goods('apple',4)
banana = Goods('banana',5)

print(apple.or_price)
print(banana.or_price)

@classmethod 是一个函数修饰符,它表示接下来的是一个类方法,而对于平常我们见到的则叫做实例方法。 类方法的第一个参数cls,而实例方法的第一个参数是self,表示该类的一个实例。 

普通对象方法至少需要一个self参数,代表类对象实例

类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。 对于类方法,可以通过类来调用,就像C.f(),有点类似C++中的静态方法, 也可以通过类的一个实例来调用,就像C().f(),这里C(),写成这样之后它就是类的一个实例了

 

@staticmethod 用法:

当一个方法要使用对象的属性时 就是用普通的方法
当一个方法要使用类中的静态属性时 就是用类方法
当一个方法要既不使用对象的属性也不使用类中的静态属性时,就可以使用staticmethod静态方法

 @staticmethod 特点:不需要像类方法或者普通的实例方法一样需要必须的参数(cls或者self).

例:

class Date:
    def __init__(self,month,day,year)
        self.mouth = mouth
        self.day = day
        self.year = year

    @staticmethod
    def is_date_valid(date_as_string):
        day,month,year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

is_date = Date.is_date_valid('11-09-2012') #此处调用 staticmethod装饰的函数,可以在类外直径传值.不用理睬self参数内的传值.

staticmethod可以像一个普通的方法被调用,它与这个类有明确的相关性,但是不需要访问这个类内部的属性或者方法.

对比:

  1. @classmethod,由于其强制要求有cls参数存在,可以更多的用于当作一个类实例工厂🏭,或者可以作为一个可以用于派生类中的构造函数;
  2. @staticmethod,如果一个方法不需要使用类内部的属性和方法,但确实和类有明确的相关性,它就可以使用@staticmethod来修饰

 

 方法调用总结:

使用什么样的方法要看具体用到了哪些名称空间中的变量
# 当一个方法要使用对象的属性时 就是用普通的方法
# 当一个方法要使用类中的静态属性时 就是用类方法
# 当一个方法要既不使用对象的属性也不使用类中的静态属性时,就可以使用staticmethod静态方法

 

posted @ 2018-04-18 16:27  tom2ling  阅读(195)  评论(0编辑  收藏  举报