Iron music learning python_day22_ object-oriented programming 4

Most of the following content is taken from the blog http://www.cnblogs.com/Eva-J/

package

[Encapsulation] Hide the properties and implementation details of the object, and only provide public access to the outside world.

[Benefits]
1. Isolate changes;
2. Ease of use;
3. Improve reusability;
4. Improve security;

[Encapsulation Principles]
1. Hide all the content that is not required to be provided externally;
2. Hide all attributes and provide public methods to access them.

Private variables and private methods

Hide attributes by starting with double underscores in python (set them to private)

私有变量

#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:

class Family:
    __money=0 
# 类的数据属性就应该是共享的,
# 但是语法上是可以把类的数据属性设置成私有的如__money,会变形为_Family__money

    def __init__(self):
        self.__x=10 # 变形为self._Family__X

    def __foo(self): # 变形为_Family__foo
        print('from Family')

    def func(self):
        self.__foo() # 只有在类内部才可以通过__foo的形式访问到.

类外部使用._Family__money是可以访问到的,
即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形。

What is encapsulation?

In a broad sense: put a bunch of things in a container, package and seal it. In a
narrow sense : (a phenomenon is given a name that belongs to it.)
Functions and properties are packed into a non-global namespace - encapsulation

定义一个私有的名字 : 就是在私有的名气前面加两条下划线 __N = 'word'
所谓私有,就是不能在类的外面去引用它。
python就是把__名字当成私有的语法

一个私有的名字 在存储的过程中仍然会出现在'类名字.__dict__'中,所以我们仍然可以调用到。
python对其的名字进行了修改: _类名__名字
只不过在类的外部调用 :需要“_类名__名字”去使用
在类的内部可以正常的使用名字。
在类内 只要你的代码遇到__名字,就会被python解释器自动的转换成_类名__名字

私有属性
class B:
    def __init__(self,name):
        self.__name = name
    def func(self):
        print('in func : %s'%self.__name)
b = B('alex')
# print(b._B__name)
b.func()

私有方法
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的。
class C:
    def __wahaha(self):
        print('wahaha')
    def ADCa(self):
        self.__wahaha()
c = C()
# c._C__wahaha()
c.ADCa()

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

class D:
    def __func(self):      # '_D__func'
        print('in func')

class E(D):
    def __init__(self):
        self.__func()      # '_E__func'
e = E()

私有的名字不能被子类继承

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

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

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

class F:
    pass
F.__name = 'alex'  # 不是在创建私有属性
print(F.__name)
print(F.__dict__)

变形只在类的内部发生

class F:
    def ADCa(self):
        self.__name = 'alex'   # _F__name

f = F()
f.ADCa()
print(f._F__name)

Comparison of java and python

public public
java can be used inside the class, subclasses can use, external can use; all normal names in python.

In java protected by protect,
it can be used inside the class, subclasses can use it, but not outside; there is no such division in python.

private private
java can only be used inside the class, neither subclasses nor outside; in python, the __ name with double underscores on the left

private usage

1) When a method does not want to be inherited by subclasses;
2) Some properties or methods do not want to be called from the outside, only to be used by internal methods.

这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,
而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,
知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形。

Packaging and Extensibility

Encapsulation is to clearly distinguish between inside and outside, so that the implementer of the class can modify the contents of the encapsulation without affecting the code of the external caller;
while the external user only knows one interface (function), as long as the interface (function) name and parameters remain unchanged, use The author's code never needs to be changed.
This provides a good basis for cooperation - or, as long as the basic convention of the interface remains the same, code changes are not a concern.

#类的设计者
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
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

# 人体BMI指数
# 体质指数(BMI)=体重(kg)÷身高^2(m)
# 写一个类 描述人体BMI指数

class Person:
    def __init__(self,name,weight,height):
        self.name = name
        self.__height = height
        self.__weight = weight
        # self.bmi = self.__weight / self.__height ** 2
        # self.bmi = self.cal_BMI()

    def cal_BMI(self):
        return self.__weight / self.__height ** 2

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

p = Person('大表哥',92,1.85)
print(p.cal_BMI())
p.cal_BMI()   # bmi是一个名词
print(p.bmi)   # bmi是一个名词
p._Person__weight = 90
print(p.bmi)

Disguising a method as a property doesn't make your code any logically better,
it just changes the way from the caller's point of view to make it look more reasonable.

Note: If it is simply calculated in init, the property will be assigned a locked value when it is instantiated.
So the Property method is needed to disguise a method as a property.

如下例:
class Person:
    def __init__(self,name,weight,height):
        self.name = name
        self.__height = height
        self.__weight = weight
        self.bmi = self.__weight / self.__height ** 2

p = Person('大表哥',92,1.85)   
print(p.bmi)   # bmi是一个名词
p._Person__weight = 90   # 修改了体重
print(p.bmi)  # 而bmi的值并没有随之变化

@property 能够将一个方法伪装成一个属性
从原来的的对象名.方法名(),变成了对象名.方法名
只是让代码变的更美观。

如果proerty装饰方法的同时有重名的属性名字:
会在实例化的时候就报错。
被property装饰的bmi仍然是一个方法 存在Person.__dict__
对象的.__dict__中不会存储这个属性。
在一个类加载的过程中,会先加载这个中的名字,包括被property装饰的
在实例化对象的时候,python解释器会先到类的空间里看看有没有这个被装饰的属性,
如果有就不能再在自己对象的空间中创建这个属性了。

Why use property

After defining a function of a class as a feature, when the object uses obj.name again, it
is impossible to notice that its name is calculated by executing a function. The use of this feature follows the principle of unified access.

ps: There are three ways of object-oriented encapsulation:
[public]
This means that it is not encapsulated, it is open to the outside world
[protected]
This encapsulation method is not open to the outside world, but is open to friends or subclasses
[private]
This type of encapsulation Encapsulation is not open to anyone

Python does not syntactically build the three of them into its own class mechanism.
In C++, all data is generally set as private,
and then set and get methods (interfaces) are provided to set and obtain, This can be achieved by the property method in python

Example of a circle class:
disguise the methods of area and perimeter as attributes:

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(10)
print(c.cal_area())
print(c.cal_perimeter())

将方法伪装成属性,方法中一般涉及的都是一些计算过程
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(10)
print(c.area)
print(c.perimeter)
c.r = 15
print(c.area)
print(c.perimeter)

一个静态属性property本质就是实现了get,set,delete三种方法
__属性 setter deleter方法
class Person:
    def __init__(self,name):
        self.__name = name  # 私有的属性

    @property
    def name(self):
        return self.__name

    def set_name(self,new_name):
        if type(new_name) is str:
            self.__name = new_name
        else:
            print('您提供的姓名数据类型不合法')

p = Person('alex')
print(p.name)
# 和直接定义name属性有什么区别???--区别是不能直接修改
p.set_name('alex_sb')
print(p.name)
p.set_name(123)
print(p.name)

方法伪装成的属性的修改
class Person:
    def __init__(self,n):
        self.__name = n  # 私有的属性了
    @property
    def name(self):
        return self.__name

    @name.setter        # 重要程度 ***
    def name(self,new_name):
        if type(new_name) is str:
            self.__name = new_name
        else:
            print('您提供的姓名数据类型不合法')

p = Person('alex')
print(p.name)       #def name(self):
p.name = 'alex_sb' #def name(self,new_name):
print(p.name)       #def name(self):
p.name = 123 #def name(self,new_name):
print(p.name)       #def name(self):

方法伪装成的属性的删除
class Person:
    def __init__(self,n):
        self.__name = n  # 私有的属性
    @property            # 重要程度 ****
    def name(self):
        return self.__name
    @name.deleter
    def name(self):
        print('name 被删除了')
    @name.deleter         # 重要程度*
    def name(self):
        del self.__name

p = Person('alex')
print(p.name)
del p.name  # 只是执行了被@name.deleter装饰的函数
print(p.name)

@property --> func     将方法伪装成属性,只读的权限
@func.setter --> func  对伪装的属性进行赋值的时候调用这个方法 一般情况下用来做修改
@func.deleter --> func 在执行del 对象.func的时候调用这个方法 一般情况下用来做删除 基本不用

例:商店商品的折扣促销活动
有一个商品 : 原价 折扣
当我要查看价格的时候 我想看到的是折后价格。
class Goods:
    def __init__(self,name,origin_price,discount):
        self.name = name
        self.__price = origin_price
        self.__discount = discount

    @property
    def price(self):
        return self.__price * self.__discount

    @price.setter
    def price(self,new_price):
        if type(new_price) is int or type(new_price) is float:
            self.__price = new_price

apple = Goods('apple',5,0.8)
print(apple.price)
# 修改苹果的原价
apple.price = 8
print(apple.price)

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

类的@classmethod用法:

例:店庆 全场八折
class Goods:
    __discount = 0.8
    def __init__(self,name,origin_price):
        self.name = name
        self.__price = origin_price

    @property
    def price(self):
        return self.__price * Goods.__discount

    @classmethod
    def change_discount(cls,new_discount):     
# 类方法 可以直接被类调用 不需要默认传对象参数 只需要传一个类参数就可以了
        cls.__discount = new_discount

Goods.change_discount(1)  # 不依赖对象的方法 就应该定义成类方法 类方法可以任意的操作类中的静态变量
apple = Goods('apple',5)
banana = Goods('banana',8)
print(apple.price)
print(banana.price)

# 折扣变了 店庆结束 恢复折扣
# apple.change_discount(1)   
# 如果要改变折扣 是全场的事情 不牵扯到一个具体的物品 所以不应该使用对象来调用这个方法
# print(apple.price)
# print(banana.price)

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

例:登录
# def login():
#     user= input('user :')
#     if user == 'alex':print('success')
#     else :print('faild')
#
# login()
class Student:
    def __init__(self,name):pass

    @staticmethod
    def login(a):                   
# login就是一个类中的静态方法 静态方法没有默认参数 就当成普通的函数使用即可
        user = input('user :')
        if user == 'alex':
            print('success')
        else:
            print('faild')

Student.login(1)

# 完全面向对象编程
# 先登录 后 实例化
# 还没有一个具体的对象的时候 就要执行login方法

Which method to use depends on which namespace variables are used.
1) When a method wants to use the properties of the object, use the normal method;
2) When a method wants to use the static properties of the class, use the class method;
3) When a method wants to use neither the properties of the object nor the use of the class method When you have static properties in a class, you can use staticmethod static methods.

end
2018-4-17

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325687131&siteId=291194637