面向过程:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用,不支持丰富的“面向对象”特性(比如继承、多态)
函数式编程:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
优点:
缺点:
应用场景:
优点:
缺点:
应用场景:
1.使程序更加容易扩展和易更改,使开发效率变的更高
2.基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
类:一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型、模板。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
属性:人类包含很多特征,把这些特征用程序来描述的话,叫做属性,比如年龄、身高、性别、姓名等都叫做属性,一个类中,可以有多个属性
方法:人类不止有身高、年龄、性别这些属性,还能做好多事情,比如说话、走路、吃饭等,相比较于属性是名词,说话、走路是动词,这些动词用程序来描述就叫做方法。
实例(对象):一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
实例化:把一个类转变为一个对象的过程就叫实例化
1,Encapsulation 封装
所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。。封装是面向对象的特征之一,是对象和类概念的主要特性。 简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
2,Inheritance 继承
所谓继承指提供了同一类对象共性的处理方法,子类继承父类共性的东西。 这样有利于代码的复用性,即子类拥有父类的方法。通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。
3,Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
封装是面向对象编程的一大特点,面向对象编程的第一步将属性和方法封装到一个抽象类中,外界使用类创建对象然后让对象调用方法,对象方法的的细节都被封装在类的内部。
【优点】
将变化隔离; 便于使用;提高复用性; 提高安全性。
【封装原则】
将不需要对外提供的内容都隐藏起来; 把属性都隐藏,提供公共方法对其访问。
派生描述了子类的创建,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义;
继承描述了子类属性从祖先类继承这样一种方式
父类也叫基类,派生类也叫子类
多态指的是同一种事物的多种状态:水这种事物有多种不同的状态:冰,水蒸气
多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。即有多种形态
冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样
泛化表示所有子类与其父类及祖先类有一样的特点;
特化描述所有子类的自定义,也就是什么属性让它与其祖先类不同
自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__
1、类是一种数据结构,我们可以用它来定义对象,对象把数据值和行为特性融合在一起
2、python使用class关键字来创建类,简单的类的声明可以是关键字后紧跟类名
3、通常类名的第一个字母大写
注意:如果没有bases,则创建经典类;bases填写 ClassName的父类;推荐使用新式类,新式类中,object是一切类的父类
1、如果说类是一种数据结构定义类型,那么实例则声明了一个这种类型的变量
2、类被实例化得到实例,该实例的类型就是这个被实例化的类
1、创建实例与调用函数类似,调用一个类就创建了它的一个实例
执行结果:
当类被调用,实例化的第一步是创建实例对象。一旦对象创建了, Python 检查是否实现了__init__()方法
默认情况下,如果没有定义或覆盖特殊方法__init__(),对实例不会施加任何特别的操作任何所需的特定操作,都需要程序员实现__init__(),覆盖它的默认行为。
如果__init__()没有实现,则返回它的对象,实例化过程完毕如果__init__()已经被实现,那么它将被调用,实例对象作为第一个参数(self)被传递进去,像标准方法调用一样。调用类时,传进的任何参数都交给了__init__()
例如:
或者:
执行结果:
在创建实例后会立即执行对象的__init__方法。该方法必须接受一个位置参数(self),然后可以接受任意数量的必要或可选位置参数,以及任意数量的关键字参数。
__init__方法并没有创建新对象,该操作由__new__完成。该方法旨在为创建后的对象提供初始化数据。
实际上__init__方法并不返回也不应该返回任何值。在Python中所有的__init__都不返回值,如果用return返回值则会导致TypeError错误。
执行结果:
执行结果:
__new__方法实际上在__init__方法之前执行,用于创建类的实例。
然而__init__方法负责在实例创建后对其进行自定义,__new__方法才是实际上创建并返回实例的方法
__new__方法永远是静态的。同样它也无需显式装饰。第一个也是最重要的参数是创建实例所需要的类,按照惯例,命名为cls。
在大多数情况下,__new__方法的其他参数会被完整复制到__init__方法中。参数在调用类构造函数时首先会被传递给__new__方法
(这是由于其先被调用),然后再传递给__init__方法。
在实际应用中,大多数类无需定义__new__方法。该方法在Python中的内置实现已经足够。
只有在通过__new__方法返回当前类的实例时才会执行__init__方法。如果返回的不是当前类的实例,那么就不会调用__init__方法。
这样做主要是因为在某些情况下返回其他类的实例,__init__也会被其他类中定义的__new__方法触发,而执行两个不同类的__init__方法很可能会导致问题。
执行结果:
总结:
注意:
执行结果:
与 __init__() 方法对应的是 __del__() 方法,__init__() 方法用于初始化 Python 对象,而 __del__() 则用于销毁 Python 对象,即在任何 Python 对象将要被系统回收之时,系统都会自动调用该对象的 __del__() 方法。
大部分时候,Python 的 ARC 都能准确、高效地回收系统中的每个对象。但如果系统中出现循环引用的情况,比如对象 a 持有一个实例变量引用对象 b,而对象 b 又持有一个实例变量引用对象 a,此时两个对象的引用计数都是 1,而实际上程序已经不再有变量引用它们,系统应该回收它们,此时 Python 的垃圾回收器就可能没那么快,要等专门的循环垃圾回收器(Cyclic Garbage Collector)来检测并回收这种引用循环。当一个对象被垃圾回收时,Python 就会自动调用该对象的 __del__ 方法。需要说明的是,不要以为对一个变量执行 del操作,该变量所引用的对象就会被回收,只有当对象的引用计数变成 0 时,该对象才会被回收。因此,如果一个对象有多个变量引用它,那么 del其中一个变量是不会回收该对象的。
程序中重写了 Item 类的 __del__() 方法,该方法就是 Item 类的析构函数,当系统将要回收 Item 时,系统会自动调用 Item 对象的 __del__() 方法。上面程序先创建了一个 Item 对象,并将该对象赋值给 im 变量,① 号代码又将 im 赋值给变量 x,这样程序中有两个变量引用 Item对象,接下来程序执行 del im 代码删除 im 对象,此时由于还有变量引用该 Item 对象,因此程序并不会回收 Item 对象。运行上面程序,可以看到如下输出结果:
从上面程序的输出结果可以看到,del im 执行之后,程序并没有回收 Item 对象,只有等到程序执行将要结束时(系统必须回收所有对象),系统才会回收 Item 对象。如果将程序中 ① 号代码注释掉,再次运行上面程序,将会看到如下输出结果:
注释掉 ① 号代码之后,当程序执行 del im 之后,此时程序中不再有任何变量引用该 Item 对象,因此系统会立即回收该对象,则无须等到程序结束之前。
最后需要说明的是,如果父类提供了 __del__() 方法,则系统重写 __del__() 方法时必须显式调用父类的 __del__() 方法,这样才能保证合理地回收父类实例的部分属性。
属性就是属于另一个对象的数据或者函数元素,可以通过句点属性标识法来访问
类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本,这个和C++中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象和实例对象访问
类有两种属性:数据属性和函数属性
例子:
要知道一个类有哪些属性,有两种方法。最简单的是使用 dir()内建函数。另外是通过访问类的字典属性__dict__,这是所有类都具备的特殊属性之一
函数 dir() 就能查看对象的属性:
dir()返回的仅是对象的属性的一个名字列表
执行结果:
它返回一个列表,包含所有能找到的属性的名字,这里我们为实例 a 创建了 abc 属性,这里就能看到了。
有些同学会有疑问,为什么我才写了几个属性,结果却多出一堆我不认识的?
因为我们这里用的是新式类,新式类继承于父类 object ,而这些我们没有写的属性,都是在 object 中定义的。
python3中不管有没有显示的去继承object,默认都会继承的
Python2中如果显示的继承object就是新式类,没有继承object就是经典类
如果我们用经典类的话:
为了方便演示,下面都使用经典类,若没有特殊说明,新旧两式基本是一样的。
使用特殊类属性__dict__来查看一下类的属性
__dict__返回的是一个字典,它的键(keys)是属性名,键值(values)是相应的属性对象的数据值
执行结果:
其中 __doc__ 是说明文档,在类中的第一个没有赋值的字符串就是说明文档,一般在class的第二行写,没有就为空字符串。
__module__ 表示这个类来自哪个模块,我们在主文件中写的类,其值应该为 '__main__',在其他模块中的类,其值就为模块名。
执行结果:
文档字符串不一定非要用三引号,只是一般习惯上用三引号表示注释。文档注释也可以使用字符串的形式,实例也可以访问,实际访问的是创建它的类的文档字符串,但是子类并不会继承父类的文档字符串,关于继承的问题以后再讲。
除了这两个特殊的属性之外,还有几个常用的,虽然没有显示出来,但也是可以用的。
__dict__
这个属性就是将对象内的属性和值用字典的方式显示出来。这里可以明显的看出来,实例创建了一个同名的属性。
__class__
得到的结果是不一样的
a.__class结果是__main__.Test ---它表示创建这个实例的类是哪个,这里显示是 __main__ 模块,也就是主文件中的 Test 这个类创建的。
Test.__class__结果是type ------表示创建类对象的元类是type
内建函数 dir()可以显示类属性,同样还可以打印所有实例属性
与类相似,实例也有一个__dict__特殊属性(可以调用 vars()并传入一个实例来获取),它是实例属性构成的一个字典:
2、这些值像静态成员那样被引用,即使在多次实例化中调用类,它们的值都保持不变3、静态成员(类的数据属性)不会因为实例而改变它们的值,除非实例中显式改变它们的值
4、类和实例都是名字空间,类是类属性的名字空间,实例则是实例属性的名称空间5、可采用类来访问类属性,如果实例没有同名的属性的话,你也可以用实例来访问
总结
核心提示:使用类属性来修改自身(不是实例属性)
1、使用实例属性来试着修改类属性是很危险的。原因在于实例拥有它们自已的属性集,在 Python 中没有明确的方法来指示你想要修改同名的类属性
2、修改类属性需要使用类名,而不是实例名
默认情况下,Python中的成员函数和成员变量都是公开的(public),在python中没有类public,private等关键词来修饰成员函数和成员变量。其实,Python并没有真正的私有化支持,但可用下划线得到伪私有。 尽量避免定义以下划线开头的变量!
在内部,python使用一种 name mangling 技术,将 __membername替换成 _classname__membername,
也就是说,类的内部定义中,所有以双下划线开始的名字都被"翻译"成前面加上单下划线和类名的形式。例如:为了保证不能在class之外访问私有变量,Python会在类的内部自动的把我们定义的__spam私有变量的名字替换成为_classname__spam
(注意,classname前面是一个下划线,spam前是两个下划线),因此,用户在外部访问__spam的时候就会提示找不到相应的变量。
python中的私有变量和私有方法仍然是可以访问的;访问方法如下:私有变量:实例._类名__变量名私有方法:实例._类名__方法名()
1、简单的模块级私有化只需要在属性名前使用一个单下划线字符。这就防止模块的属性用from mymodule import *来加载。
2、这是严格基于作用域的,所以这同样适合于函数。
3、 在一个模块中以单下划线开头的变量和函数被默认当作内部函数;如果使用from a_module import * 导入时,这部分变量和函数不会被导入。
不过值得注意的是,如果使用 import a_module 这样导入模块,仍然可以用a_module._some_var 这样的形式访问到这样的对象
Python中没有真正的私有属性或方法,可以在你想声明为私有的方法和属性前加上单下划线,以提示该属性和方法不应在外部调用.如果真的调用了也不会出错,但不符合规范.
下面看一下例子:
执行结果:
只要是带着下划线的,全部都是未定义错误
现在再看一个例子:import test
这样就可以使用了。
类A中定义了一个_method方法,按照约定是不能在类外面直接调用它的为了可以在外面使用_method方法,又定义了method方法,method方法调用_method方法。但是加了_的方法也可以在类外面调用:
1、由双下划线开始的属性在运行时被“混淆”,所以直接访问是不允许的。实际上,会在名字前面加上下划线和类名 。把类名加上后形成的新的“混淆”结果将可以防止在祖先类或子孙类中的同名冲突。
这种名字混淆的另一个目的,是为了保护__XXX 变量不与父类名字空间相冲突。如果在类中有一个__XXX 属性,它将不会被其子类中的__XXX 属性覆盖。
使用__XXX,子类的代码就可以安全地使用__XXX,而不必担心它会影响到父类中的__XXX。
执行结果:
如果坚持要访问的话,可以这样做:不建议
执行结果:
例子:
在类A中,__method方法其实由于name mangling技术的原因,变成了_A__method,所以在A中method方法返回的是_A__method,B作为A的子类,只重写了__method方法,并没有重写method方法,所以调用B中的method方法时,调用的还是_A__method方法:因此,在我们创建一个以"__"两个下划线开始的方法时,这意味着这个方法不能被重写,它只允许在该类的内部中使用。
可以这样调用
在B中重写method
双下划线开头双下划线结尾的是一些 Python 的特殊对象,如类成员的 __init__、__del__、__add__、__getitem__等。 Python 官方推荐永远不要将这样的命名方式应用于自己的变量或函数,而是按照文档说明来使用。注意,私有化都是针对外部而言,在类内部,依然可以使用正常的访问方式,在类的外部就必须进行“混淆”了
在程序设计中,封装(Encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其含义是其他程序无法调用。
要了解封装,离不开“私有化”,就是将类或者是函数中的某些属性限制在某个区域之内,外部无法调用。
封装数据的主要原因是:保护隐私(把不想别人知道的东西封装起来)
封装方法的主要原因是:隔离复杂度(比如:电视机,我们看见的就是一个黑匣子,其实里面有很多电器元件,对于用户来说,我们不需要清楚里面都有些元件,电视机把那些电器元件封装在黑匣子里,提供给用户的只是几个按钮接口,通过按钮就能实现对电视机的操作。)
封装其实分为两个层面,但无论哪种层面的封装,都要对外界提供好访问你内部隐藏内容的接口(接口可以理解为入口,有了这个入口,使用者无需且不能够直接访问到内部隐藏的细节,只能走接口,并且我们可以在接口的实现上附加更多的处理逻辑,从而严格控制使用者的访问)
第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装。
注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口
第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。
封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用,外部要想用类隐藏的属性,需要我们为其开辟接口,让外部能够间接地用到我们隐藏起来的属性(隐藏+对外接口)
在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
即在准备私有化的属性(包括方法、数据)名字前面加两个下划线即可。
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:(所以在外部不能用__x来调用了,用_类名__x来调用时可以的)
变形需要注意的问题:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形,主要用来限制外部的直接访问。
2.变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形
3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
正常情况下:
看一下把fa被定义成私有的情况:
1. 你的身体没有一处不体现着封装的概念:你的身体把膀胱尿道等等这些尿的功能隐藏了起来,然后为你提供一个尿的接口就可以了(接口就是你的。。。,),你总不能把膀胱挂在身体外面,上厕所的时候就跟别人炫耀:hi,man,你瞅我的膀胱,看看我是怎么尿的。
2. 电视机本身是一个黑盒子,隐藏了所有细节,但是一定会对外提供了一堆按钮,这些按钮也正是接口的概念,所以说,封装并不是单纯意义的隐藏!!!
3. 快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了
提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。
注意:
python并不会真的阻止你访问私有的属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import *时不能被导入,但是你from module import _private_module依然是可以导入的
其实很多时候你去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home,sys._clear_type_cache),这些都是私有的,原则上是供内部调用的,作为外部的你,一意孤行也是可以用的,只不过显得稍微傻逼一点点
python要想与其他编程语言一样,严格控制属性的访问权限,只能借助内置方法如__getattr__
1、什么是特性property
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值(就是一个装饰器)
注意:被property装饰的属性会优先于对象的属性被使用,而被propery装饰的属性,分成三种:property、被装饰
的函数名.setter、被装饰的函数名.deleter(都是以装饰器的形式)。
注意:此时的特性arear、perimeter和volume不能被赋值。
2、为什么要用property
计算出来的,这种特性的使用方式遵循了统一访问的原则。
除此之外,看下
例子:
python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的
,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现。
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一
个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说
,只要接口这个基础约定不变,则代码改变不足为虑。
扩展原有的代码,使功能增加:
对于类的使用者,仍然在调用area接口的人来说,根本无需改动自己的代码,就可以用上新功能: