以下内容大部分摘自博客http://www.cnblogs.com/Eva-J/

封装

【封装】隐藏对象的属性和实现细节,仅对外提供公共访问方式。

【好处】

  1. 将变化隔离;
  2. 便于使用;
  3. 提高复用性;
  4. 提高安全性;

【封装原则】

  1. 将不需要对外提供的内容都隐藏起来;
  2. 把属性都隐藏,提供公共方法对其访问。

私有变量和私有方法

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

私有变量

#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如__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是可以访问到的,
即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形。

何为封装?

广义上的 :把一堆东西装在一个容器里,打包封起来

狭义上的 :(对一种现象起一个专门属于它的名字。)

函数和属性装到了一个非全局的命名空间 —— 封装

定义一个私有的名字 : 就是在私有的名气前面加两条下划线 __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)

java与python的对比

public 公有的

java中在类的内部可以使用,子类可以使用,外部可以使用;python中所有正常的名字。

protect 保护的

java中在类的内部可以使用,子类可以使用,外部不可以使用;python中没有此划分。

private 私有的

java中只能在类的内部使用,子类和外部都不可以使用;python中表现为左边双下划线的__名字

私有的用法

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

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

这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,
而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。 这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,
知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形。

封装与扩展性

封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;

而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。

这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。

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

将一个方法伪装成一个属性,并不会让你的代码有什么逻辑上的提高,

只是从调用者的角度上换了一种方式,使之看起来更合理。

注意:单纯的在init中计算的话,属性会在实例化时便会被赋值锁死数值。

所以需要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解释器会先到类的空间里看看有没有这个被装饰的属性,
如果有就不能再在自己对象的空间中创建这个属性了。

为什么要用property

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,

根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

ps:面向对象的封装有三种方式:

【public】

这种其实就是不封装,是对外公开的

【protected】

这种封装方式对外不公开,但对朋友(friend)或者子类公开

【private】

这种封装对谁都不公开

python并没有在语法上把它们三个内建到自己的class机制中,

在C++里一般会将所有的所有的数据都设置为私有的,

然后提供set和get方法(接口)去设置和获取,在python中通过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(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方法

使用什么样的方法要看具体用到了哪些名称空间中的变量。

1)当一个方法要使用对象的属性时 就是用普通的方法;

2)当一个方法要使用类中的静态属性时 就是用类方法;

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

end

2018-4-17

铁乐学python_day22_面向对象编程4的更多相关文章

  1. 铁乐学python_day18-19_面向对象编程1

    以下笔记绝大部分(百分之80或以上)摘自我的授课老师之一:老男孩教育中的景老师. 她上课讲的知识点由浅入深,引人入胜,听她的课完全不会感觉到困阿,而且不知不觉中就感觉掌握了. 她的博客是: http: ...

  2. 铁乐学python_day20_面向对象编程2

    面向对象的组合用法 软件重用的重要方式除了继承之外还有另外一种方式,即:组合 组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合. 例:人狗大战,人类绑定上武器来对狗进行攻击: # 定 ...

  3. 铁乐学python_day21_面向对象编程3

    抽象类和接口类 以下内容大部分摘自博客http://www.cnblogs.com/Eva-J/ 继承有两种用途: 一:继承基类的方法,并且做出自己的改变或者扩展(代码重用) 二:声明某个子类兼容于某 ...

  4. 铁乐学Python_Day33_网络编程Socket模块1

    铁乐学Python_Day33_网络编程Socket模块1 部份内容摘自授课老师的博客http://www.cnblogs.com/Eva-J/ 理解socket Socket是应用层与TCP/IP协 ...

  5. 铁乐学python_day24_面向对象进阶1_内置方法

    铁乐学python_day24_面向对象进阶1_内置方法 题外话1: 学习方法[wwwh] what where why how 是什么,用在哪里,为什么,怎么用 学习到一个新知识点的时候,多问问上面 ...

  6. 铁乐学python_day23_面向对象进阶1_反射

    铁乐学python_day23_面向对象进阶1_反射 以下内容大部分摘自博客http://www.cnblogs.com/Eva-J/ isinstance()和issubclass() 两者的返回值 ...

  7. 洗礼灵魂,修炼python(34)--面向对象编程(4)—继承

    前面已经说到面向对象编程有封装,继承,多态三大特性,那么其中的继承则很重要,可以直接单独的拿出来解析 继承 1.什么是继承: 字面意是子女继承父母的家产或者特性等.而在编程里继承是指子类继承父类(基类 ...

  8. 铁乐学python_Day44_IO多路复用

    目录 IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) 多路复用IO(IO multiplexing) 异步IO(Asynchronous I/O) IO ...

  9. 铁乐学python_Day43_协程

    铁乐学python_Day43_协程 引子 之前我们学习了线程.进程的概念,了解了在操作系统中进程是资源分配的最小单位,线程是CPU调度的最小单位. 按道理来说我们已经算是把cpu的利用率提高很多了. ...

随机推荐

  1. HihoCoder - 1478 水陆距离

    水陆距离 描述 给定一个N x M的01矩阵,其中1表示陆地,0表示水域.对于每一个位置,求出它距离最近的水域的距离是多少. 矩阵中每个位置与它上下左右相邻的格子距离为1. 输入 第一行包含两个整数, ...

  2. HihoCoder - 1040 矩形判断

    矩形判断 给出平面上4条线段,判断这4条线段是否恰好围成一个面积大于0的矩形. Input 输入第一行是一个整数T(1<=T<=100),代表测试数据的数量. 每组数据包含4行,每行包含4 ...

  3. 五个步骤教你理清Redis与Memcached的区别

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由Super发表于云+社区专栏 memcached和redis,作为近些年最常用的缓存服务器,相信大家对它们再熟悉不过了.前两年还在学校 ...

  4. nodejs zip压缩版安装与配置

    Node.js 1.下载 下载地址:https://nodejs.org/zh-cn/download/ 选择相应的版本下载 2.解压缩 将文件解压到要安装的位置,并新建两个目录 node-globa ...

  5. I/O模式总结

    进程读取数据时要经过两个阶段: 1.等待内核准备数据: 2.将内核缓冲区中的数据复制到进程缓冲区中. 一.阻塞IO 进程会阻塞在等待内核准备数据和数据从内核空间复制到用户空间这两个阶段. 二.非阻塞I ...

  6. Mysql5.5升级到5.6步骤详解 小版本大版本

    http://blog.csdn.net/i_team/article/details/9935693 小版本升级,先关闭数据库,然后mv直接全部替换掉mysql目录下的bin/ ,lib/ ,sha ...

  7. JavaScript shift()函数移出数组第一个数据

    pop() 函数用来移出数组中最后一个元素.如果想要移出第一个元素要怎么办呢? .shift() 就是专门用来处理这类型需求的.它的工作原理类似 .pop(),但它移除的是第一个元素,而不是最后一个.

  8. [C语言] 数据结构-预备知识动态内存分配

    动态内存分配 静态内存分配数组 int a[5]={1,2,3,4,5}  动态内存分配数组 int len=5; int *parr=(int *)malloc(sizeof(int) * len) ...

  9. SSM(Spring+SpringMVC+Mybstis)搭建,写给新手

    SSM框架——详细整合教程(Spring+SpringMVC+MyBatis) 作用: SSM框架是spring MVC ,spring和mybatis框架的整合,是标准的MVC模式,将整个系统划分为 ...

  10. 整理 node-sass 安装失败的原因及解决办法

    npm install 时偶尔遇到报错:没有安装python或node-sass 安装失败的问题,百度之后发现是被墙了,但根据百度的方法换了淘宝镜像和用了vpn都安装失败,最后发现原来是因为没有卸载之 ...