zoukankan      html  css  js  c++  java
  • 面向对象 ---封装

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

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

    【好处】

    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

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

  • 相关阅读:
    webkit之滚动条美化
    意想不到的javascript
    html5 的存储
    javascript 中的number
    javascript 模板
    关于ajax的跨域
    一个菜鸟眼中的前端
    【转】python
    [转]修改python默认的编码方式
    搞科研
  • 原文地址:https://www.cnblogs.com/biluo/p/7881921.html
Copyright © 2011-2022 走看看