封装 :把一些属性和方法放到类里 这本身就是一种封装    

       # 封装 : 把属性和方法藏在类里 我只能在类内部调用,不能再外部使用

【好处】

1. 将变化隔离;

2. 便于使用;

3. 提高复用性;

4. 提高安全性;

【封装原则】      

1. 将不需要对外提供的内容都隐藏起来;      

2. 把属性都隐藏,提供公共方法对其访问。

私有变量和私有方法

在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

# class Dog:
#     __role = 'dog'   #私有的静态属性
# print(Dog._Dog__role) #在外部调  #从类的外面不能直接调用,在类内的使用加上了一层密码:_类名

 

class Dog:
    __role = 'dog'   #私有的静态属性
    def func(self):
        print(Dog.__role)  #在内部调  理解为_类名__名字,类里面省掉了_类名,外部需要手动加上_类名
        
d = Dog()
d.func()
输出;
dog 

方法也可以私有起来:

class Dog:
    __role = 'dog'  # 私有的静态属性
    def __func(self):  # 私有的方法
        print("in_func")

d = Dog()
# d.func() 这样调调不到
d._Dog__func()    #这样调用

在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

 

#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如__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,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

 

#定义一个私有变量\属性\方法 : __名字
#在类的内部可以直接用 : __名字
#在类的外部不能直接使用,如果一定要用,在私有方法之前加上:_类名,变成 _类名__名字
#在类外的名字 通过__dict__就可以查看

例子:

class Room:
    def __init__(self, name, price, length, width):
        self.name = name
        self.price = price
        self.__length = length
        self.__width = width
    def area(self):
        return self.__length*self.__width

house = Room("小超超", 1000000, 2, 1)
print(house.price)
print(house.area())

输出;
1000000
2

#私有的
#私有的静态属性、方法、对象属性
#使用__名字的方式调用,保证在类内部可以调用,外部不行

class A:
    def __func(self):   #_A__func()
        print('__a_func')

class B(A):        
    def __init__(self):    
        self.__func()   #_B__func()

b = B()

输出:
报错

结论:
#私有的 不能被继承
# 当有一个名字,不想被外部使用也不想被子类继承,只想在内部使用的时候就定义私有的

 

封装与扩展性
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。

#类的设计者
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()

对象继承与property:
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

计算李岩的bmi指数:

class Person:
    def __init__(self, name, height, weight):
        self.name = name
        self.height = height
        self.__weight = weight

    def bmi(self):
        return self.__weight / (self.height ** 2)

li = Person('李岩',1.75,85)
print(li.bmi())
输出;
27.755102040816325

 

#添加一个@property
class Person:
    def __init__(self, name, height, weight):
        self.name = name
        self.height = height
        self.__weight = weight

    @property
    def bmi(self):
        return self.__weight / (self.height ** 2)

li = Person('李岩',1.75,85)
print(li.bmi)     #bim方法被伪装成一个属性
# print(li.bmi()) 这时候不能这么调用

输出;
27.755102040816325

为什么要用property
#@property 把一个方法 伪装成一个属性
#1.属性的值 是这个方法的返回值
#2.这个方法不能有参数了

将一个类的方法伪装成属性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个方法然后计算出来的,这种特性的使用方式遵循了统一访问的原则

例子; 1.圆的周长和面积;

from math import pi
class Circle:
    def __init__(self, radius): #圆的半径radius
        self.r = radius
    @property
    def area(self):
        return pi * self.r**2 #计算面积
    @property
    def perimeter(self):
        return 2*pi*self.r #计算周长

c=Circle(10)
print(c.r)
print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
print(c.perimeter) #同上

'''
输出结果:
10
314.1592653589793
62.83185307179586
'''

2.超市店庆打折;
全场8折:
私有属性与@property 配合

class Goods:
    discount = 0.8   #静态属性
    def __init__(self, name, price):
        self.__name = name
        self.__price = price  #原价

    @property
    def price(self):   #折后价
        return self.__price * Goods.discount


apple = Goods('苹果', 10)
print(apple.price)
banana = Goods("香蕉", 2.5)
print(banana.price)


输出:
8.0
2.0

 

class Goods:
    discount = 0.8   #静态属性
    def __init__(self, name, price):
        self.__name = name
        self.__price = price  #原价

    @property
    def price(self):   #折后价
        return self.__price * Goods.discount


apple = Goods('苹果', 10)
print(apple.price)
banana = Goods("香蕉", 2.5)
print(banana.price)

 

让价格商品价格可改:
class Goods:
    discount = 0.8   #静态属性
    def __init__(self, name, price):
        self.__name = name
        self.__price = price  #原价


    @property                 #获取name
    def name(self):
        return self.__name

    @name.setter            #修改name
    def name(self,new_name):
        self.__name = new_name

    @property                 #获取price
    def price(self):   #折后价
        return self.__price * Goods.discount

    @price.setter    # 让商品价格可以改  修改price
    def price(self, new_price):   #修改原价
            # if type(new_price) is int: #在方法里写一些逻辑判断,维护了代码中的属性的安全性  
            self.__price = new_price

apple = Goods('苹果', 10)
apple.price = 6     #使用了setter
print(apple.price)  #使用了property

#封装
# __私有+property
#让对象的属性变得更安全了
#获取到的对象的值可以进行一些加工
@属性.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()
print(f1.AAA)     #property  这个地方表面上看是拿到了一个属性,实际上是调用了一个方法,方法有返回值,就是我的属性值,没有返回值,默认返回值None
f1.AAA='aaa'  #setter
del f1.AAA    #deleter

输出:
get的时候运行我啊
None
set的时候运行我啊
delete的时候运行我啊

 

deleter  怎么用;(非常不常用)

class Goods:
    __discount = 0.8   #静态属性
    def __init__(self,name,price):
        self.__name = name
        self.__price = price  #原价
    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self,new_name):
        self.__name = new_name

    @name.deleter
    def name(self):
        del self.__name

    @property
    def price(self):   #折后价
        return self.__price * Goods.__discount

    @price.setter
    def price(self,new_price):   #修改原价
        if type(new_price) is int:
            self.__price = new_price


apple = Goods('苹果', 10)
del apple.name

classmethod和staticmthod方法
#定义一个类
#类里面的方法
#并没有用到self

class Goods:
    __discount = 0.8
    def change_discount(self,new_discount):
        Goods.__discount = new_discount
apple = Goods()
apple.change_discount(0.75)

上面例子,改动了一个类的属性,却要实例化一个对象来操作,很诡异,类的属性不是该对象特有的

 

class Goods:
    __discount = 0.8
    @classmethod   #类方法
    def change_discount(cls, new_discount): #self不传了,换成cls(cls理解为Goods),
        cls.__discount = new_discount
    @classmethod
    def get_discount(cls):
        return cls.__discount
Goods.change_discount(0.75)
print(Goods.get_discount())

输出;
0.75

#类方法
    #调用:不需要实例化 直接用类名调用就好
    #定义:不用接受self参数,默认传cls,cls就代表当前方法所在的类
#什么时候用类方法?
    #需要使用静态变量 且 不需要和对象相关的任何操作的时

 

#静态方法
#如果这个方法 既不需要操作静态变量
           # 也不需要使用对象相关的操作,
# 就使用静态方法

class A:
    @staticmethod
    def func(name):  #静态方法
        print(123)
A.func('alex')

#面向对象编程:
#整篇代码都是面向对象的,不可以在你的程序里面写除了类以外的其他东西,不可以定义函数,所有的函数必须定义到类里面,但是有的函数跟这些类都不产生关系,不需要实例化,就可以直接执行,这样的函数,不需要定义成需要实例化的函数,直接定义成一个静态函数,放到类里面。
#专门为面向对象编程提供的一个方法——staticmethod
#它完全可以当做普通函数去用,只不过这个函数要通过   类名.函数名   调用
#其他 传参 返回值 完全没有区别       
       
#类里面,一共可以定义这三种方法:
具体使用哪种方法,是根据你在这个方法里需要用到的变量来定的,
#普通方法 self  #使用对象名去调用
#类方法 cls  #用到类里面的变量  @classmethod
#静态方法    # 啥都没用上     @staticmethod

class A:
    @staticmethod
    def func1(name):  #静态方法
        print(123)

    @classmethod
    def func2(cls):  # 类方法
        print(123)

    def func3(self):pass
a = A()
print(a.func1)  #静态方法  
print(a.func2)  #类方法 : 绑定到A类的func
print(a.func3)  #普通方法:绑定到A类对象的func

#静态方法和类方法 都是直接可以使用类名调用
#普通方法:对象调用