面向对象的概述

  • 面向过程:根据业务逻辑从上到下写代码
  • 函数式:将其功能代码封装到函数中,日后便无需编写,仅仅调用即可 【执行函数】
  • 面向对象:对函数进行分类和封装。【创建对象】==》【通过对象执行方法】

创建类和对象

面向对象编程是一种编程方式,此编程方式需要使用“类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。

  • 类就是一个模板,模板里可以包含多个函数,函数里实现一些功能
  • 对象则是根据模板创建的实例,通过实例对象可以执行类中的函数
 #创建类
class Foo: #创建类中的函数
def Bar(self):
print('Bar') def Hello(self,name):
print(name) #根据Foo创建对象obj
obj = Foo()
obj.Bar() #执行Bar方法
obj.Hello("null") #执行Hello方法

面向对象的三大特性

  • 封装
  • 继承
  • 多态

一、封装

封装,顾名思义,就是将内容封装到某个地方,以后再去调用被封装在某处的内容

所以,在使用面向对象的封装特性时,需要:

  • 将内容封装到某处
  • 从某处调用被封装的内容

第一步:将内容封装到某处

 #创建类
class Foo: def __init__(self,name,age): #构造方法
self.name = name
self.age = age #根据Foo创建对象
#自动执行了Foo类的__init__方法
obj1 = Foo('null',66)
#此时就是分别将"null",66分别封装到obj1 self的name和age中

代码

self是一个形式参数当执行 obj1 = Foo('null',66)时,self为obj1

同理,如果obj2 = Foo("nullnull",88)时,self为obj2

所以,内容其实被封到了对象obj1和obj2中,每个对象中都有name和age属性

 第二步:从某处调用被封装的内容

调用封装内容时,有两种情况:

  • 通过对象直接调用
  • 通过self间接调用

1.通过对象直接调用被封装的内容

print(obj1.name)

print(obj1.age)

2.通过self调用被封装的内容

class Foo:

    def __init__(self,name,age):
self.name = name
self.age = age def task(self):
print(self.name)
print(self.age) obj = Foo('nullnull',66)
obj.task()

综上所述:

对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或通过self间接获取被封装的内容

二、继承

继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容

例如:

  人类:走路,说人话,吃饭

  外形人:走路,说外星话,吃饭

 class 人类:

     def 走路:
pass def 说人话:
print("人话") def 吃饭:
pass class 外星人:
def 走路:
pass def 说人话:
print("外星话") def 吃饭:
pass

示例:伪代码

在上面的代码中,我们会发现:人类和外星人都可以 走路、吃饭。通过继承,我们可以这样做...

  生物(外星人算吗?): 走路、吃饭

  人类:说人话

  外星人:说外星话

 class 生物:

     def 走路:
pass def 吃饭:
pass class 人类(生物): def 说人话:
print('人话') class 外星人(生物): def 说外星话:
print('外星话')

示例:伪代码

 class biological:

     def walk(self):
pass def eat(self):
pass class human(biological): def speak(self):
print('人话') class aliens(biological): def speak(self):
print('外星话')

示例:真代码

综上所述,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类只需要继承父类而不必一一实现每一个方法。

那么,多继承呢?

1.Python的类是可以继承多个类的,Java和C#都是只能继承一个。

2.Python如果继承了多各类,那么它寻找方法的方式有两种,分别是:深度优先,广度优先

  • 当类是经典类时,多继承情况下,会按照深度优先的方式查找
  • 当类是新式类时,多继承情况下,会按照广度优先的方式查找

如何分辨经典类和新式类呢?

看一下父类最终是否继承与object,不继承于object是经典类,继承object是新式类

class D:

    def bar(self):
print 'D.bar' class C(D): def bar(self):
print 'C.bar' class B(D): def bar(self):
print 'B.bar' class A(B, C): def bar(self):
print 'A.bar' a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> D --> C
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()

示例:经典类的继承

 class D(object):

     def bar(self):
print 'D.bar' class C(D): def bar(self):
print 'C.bar' class B(D): def bar(self):
print 'B.bar' class A(B, C): def bar(self):
print 'A.bar' a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()

示例:新式类的继承

  • 经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
  • 新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错

注:在查找过程中,一旦找到,就会立刻中断查找。

三、多态

 class F1:
pass class S1(F1): def show(self):
print 'S1.show' class S2(F1): def show(self):
print 'S2.show' def Func(obj):
print obj.show() s1_obj = S1()
Func(s1_obj) s2_obj = S2()
Func(s2_obj)

多态的代码示例

四、小结

  • 面向对象是一种编程方式,此编程方式的实现是基于对类和对象的使用
  • 类 是一个模板,模板中包含了多个“函数”,又叫方法以供使用
  • 对象,根据模板创建的实例,示例用于调用被包装在类中的函数
  • 面向对象的三大特性:封装、继承、多态

类的成员

类的成员可以分为三个大类:

  • 字段
  • 方法
  • 属性

注:所有成员中,只有普通字段的内容保存在对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。

一、字段

字段包括:普通字段和静态字段,本质的区别是在内存中保存的位置不同

  • 普通的字段属于对象
  • 静态的字段属于类
 class Province:

     # 静态字段
country = '中国' def __init__(self, name): # 普通字段
self.name = name # 直接访问普通字段
obj = Province('河南省')
print obj.name # 直接访问静态字段
Province.country

由上述代码可以看出,普通字段需要通过对象来访问静态方法通过类访问,在使用上可以看出普通字段和静态字段的归属都是不同的。

  • 静态字段在内存中只保存一份
  • 普通字段在每个对象中都要保存一份

应用:通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

二、方法

方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于其调用的方式不同。

  • 普通方法:由对象调用,至少一个self参数;执行普通方法时,自动调用该方法的对象赋值给self
  • 类方法:由类调用;至少一个cls参数;执行方法时,将自动调用该方法的类赋值给cls
  • 静态方法:由类调用,无默认参数
class Foo(object):

    def __init__(self,name):
self.name = name def ord_func(self):
'''定义普通的方法,至少要由一个self参数''' print("这是一个普通方法") @classmethod
def class_func(cls):
'''定义类方法,至少有一个cls参数'''
print("类方法") @staticmethod
def static_func():
'''定义静态方法,无默认的参数'''
print("静态方法") #调用普通方法
f = Foo()
f.ord_func() #调用类方法
Foo.class_func() #调用静态方法
Foo.static_func()
  • 相同点:对于所有的方法而言,均属于类中,所以,在内存中也只保存了一份
  • 不同点:方法调用不同、调用方法时自动传入的参数也不同

三、属性

Python中属性就是普通方法的变种

1.属性的基本使用

#定义
class Foo(object): def func(self):
pass #定义属性
@property
def prop(self):
pass #调用
obj = Foo() obj.func()
obj.prop #调用属性
  • 定义时,在普通方法的基础上添加@property装饰器
  • 定义时,属性仅有一个self参数
  • 调用时,无需括号

属性存在的意义:访问属性时可以制造出和访问字段完全相同的假象

属性存在的功能:属性内部进行一系列的逻辑计算,最终将计算结果返回

2.属性定义的方式

两种方式:

  • 装饰器
  • 静态字段:在类中定义值为property对象的静态字段

装饰器的方式

经典类,具有一种@property装饰器,用法如  属性的基本使用  中的例子

新式类,具有三种@property装饰器

 #定义
class Foo(object): #新式类 @property
def price(self):
print('@property') @price.setter
def price(self,value):
print('@price.setter') @price.deleter
def price(self):
print("@price.deleter") #调用
obj = Foo() obj.price #调用Foo中@property装饰器装饰的方法 obj.price = 1 #调用Foo中@price.setter装饰器装饰的方法,并将1当成参数传递过去 del obj.price #调用Foo中@price.deleter装饰器装饰的方法

经典类中的属性只有一种访问方式,其对应被@property修饰的方法

新式类中的属性具有三种访问方式,分别对应了三个被@property,@方法名.setter,@方法名.deleter修饰的方法

 class Goods(object):

     def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8 @property
def price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price @price.setter
def price(self, value):
self.original_price = value @price.deltter
def price(self, value):
del self.original_price obj = Goods()
obj.price # 获取商品价格
obj.price = 200 # 修改商品原价
del obj.price # 删除商品原价

新式类的实例

静态字段方式,创建值为Property对象的静态字段

class Foo:

    def bar(self):
return "nullnull" BAR = property(Bar) obj = Foo()
result = obj.BAR #会自动调用Bar方法,并获取返回值

property的构造方法中有四个参数:

  • 第一个参数是方法名,调用对象.属性时自动触发执行
  • 第二个参数是方法名,调用对象.属性 = xxx时触发
  • 第三个参数是方法名,调用del 对象.属性 时触发
  • 第四个参数是字符串,调用对象.属性.__doc__,此参数是该属性的描述信息
 class Foo:

     def Bar(self):
return "nullnull" #必须是两个参数
def set_Bar(self,value)
return "set_Bar"+value def del_Bar(self):
return "null" BAR = property(Bar,set_Bar,del_Bar,"描述信息.....") obj = Foo() obj.BAR
obj.BAR = "null"
del Foo.BAR
obj.BAR.__doc__

类成员修饰符

对于每一个类的成员而言都有两种形式:

  • 共有成员,在任何地方都能访问
  • 私有成员,只有在类的内部才能调用的方法
class Foo:

    def __init__(self):
self.name = "公有字段"
self.__name = "私有字段"
  • 公有静态字段:类可以访问;类的内部可以访问;派生类(子类)可以访问
  • 私有静态字段:仅类的内部可以访问
#########公有静态字段############
class A: name = "共有静态字段" def func(self):
print(A.name) class B(A): def show(self):
print(A.name) A.name #通过类直接访问 obj = A()
obj.func() #类的内部调用 obj_son = B()
obj_son.show() #派生类中调用 #########私有静态字段############
class C: __name = "私有静态字段" def func(self):
print(C.__name) class D(C): def show(self):
print(C.__name) C.__name #通过类进行访问 ====>错误 obj = C()
obj.func() #在类的内部调用 ====>正确 obj_son = D()
obj_son.show() #在派生类中调用 ====>错误
  • 公有普通字段:对象可以访问;类的内部可以访问;派生类中可以访问
  • 私有普通字段:仅类的内部可以访问
#############公有字段##############
class C: def __init__(self):
self.foo = "公有字段" def func(self):
print self.foo  # 类内部访问 class D(C): def show(self):
print self.foo # 派生类中访问 obj = C() obj.foo # 通过对象访问
obj.func() # 类内部访问 obj_son = D();
obj_son.show() # 派生类中访问
#############私有字段##############
class C: def __init__(self):
self.__foo = "私有字段" def func(self):
print self.foo  # 类内部访问 class D(C): def show(self):
print self.foo # 派生类中访问 obj = C() obj.__foo # 通过对象访问 ==> 错误
obj.func() # 类内部访问 ==> 正确 obj_son = D();
obj_son.show() # 派生类中访问 ==> 错误

例子同上

类的特殊成员

1.__doc__

  表示类的描述信息

class Foo:
'''描述类信息,这里是一个描述''' def func(self):
pass print(Foo.__doc__)

2.__module__和__class__

  • __module__表示当前操作在哪一个模块
  • __class__表示当前操作的对象的类是什么

3.__init__

  构造方法,通过类创建对象时,自动触发执行

4.__del__

  析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般是无需定义的,应为Python是一个高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行的,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

5.__call__

  对象后面加括号,触发执行

注:构造方法的执行是由创建对象触发的,即:对象 = 类名();

  而对于__call__方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

6.__dict__

  类或对象中的所有成员

class Province:

    country = 'China'

    def __init__(self, name, count):
self.name = name
self.count = count def func(self, *args, **kwargs):
print 'func' # 获取类的成员,即:静态字段、方法、
print Province.__dict__
# 输出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None} obj1 = Province('HeBei',10000)
print obj1.__dict__
# 获取 对象obj1 的成员
# 输出:{'count': 10000, 'name': 'HeBei'} obj2 = Province('HeNan', 3888)
print obj2.__dict__
# 获取 对象obj1 的成员
# 输出:{'count': 3888, 'name': 'HeNan'}

7.__str__

  如果一个类中定义了__str__方法,那么在打印 对象 时,默认输入该方法的返回值。

class Foo:

    def __str__(self):
return "nullnull" obj = Foo()
print(obj)

8.__getslice__、__setslice__、__delslice__

都是用于分片操作

 class Foo(object):

     def __getslice__(self,m,n):
print('这里是__getslice__',m,n) def __setslice__(self,m,n):
print('这里是__setslice__',m,n) def __delslice__(self,m,n):
print('这里是__delslice__',m,n) obj = Foo() obj[1:2] #会触发 get,即 __getslice__方法
obj[1:2] = [1,2,3] #会触发set,即__setslice__方法
del obj[1:2] #会触发del,即__defslice__方法

9.__getitem__,__setitem__,__delitem__

用法基本同上,上述是对列表,这个是对字典

10.__iter__

你知道一个列表的内部是如何支持for循环的吗?

应为列表对象中有一个方法叫做 __iter__

其实:当你的class中存在了一个__iter__方法,那么你的对象就是支持遍历的

class Foo1(object):
pass obj1 = Foo1()
for i1 in obj1:
print(i1)
#此时会报一个TypeError的错误
#'Foo' object is not iterable class Foo2(object):
def __iter__:
pass obj2 = Foo2()
for i2 in obj2:
print(i2)
#此时依旧会报一个TypeError的错误
#但却是iter() returned non-iterator of type "NoneType" class Foo3(object):
def __init__(self,t):
self.t = t def __iter__:
return iter(self.t) #当你for循环的时候,实质上是循环了item([1,2,3]) obj3 = Foo3([1,2,3])
for i3 in obj3:
print(i3)

11.__new__和__metaclass__

__new__与__init__方法相类似,都是在实例化对象的时候自动调用

注意:如果同时存在__init__和__new__,则__new__的优先级要高于__init__

 class Singleton(object):

     def __new__(cls,*args,**kwargs):
if not hasattr(cls,"_instance"):
cls._instance = super(Singleton,cls).__new__(cls,*args,**kwargs)
return cls._instance

通过__new__实现单例

class Foo(object):
def __init__(self):
pass obj = Foo()

上面是一个最简单那的对象的实例化。

obj是通过Foo类实例化的一个对象。

但是要知道Python中一切事物皆对象,这样,Foo也应该是一个对象,那么,问题来了........

这样来说,obj是通过Foo实例化而来的,那么Foo类对象也应该是通过执行某个类的构造方法创建的吧。

#我们使用type来分别看一下

type(obj)

type(Foo)

     

这就表示

  obj对象是Foo类的一个实例

  Foo类对象是type类的一个实例

所以说,Foo类对象是通过type类的构造方法创建的

那么,我们就可以通过两种不同的方法去创建类:

(1).我们使用class常规的创建

(2).使用type类的构造函数来创建

########普通方法############
class Foo(object):
def func(self):
print('这其实是一个方法') ########type创建###########
def func(self):
print('这其实是一个方法') Foo = type('Foo',(object,),{'func':func})
#type的参数讲解
#第一个:类名
#第二个:当前类的基类
#第三个:类的成员,方法

以下是一个实验..................

这个过程其实就是Type创建类的过程.......

我的Python升级打怪之路【六】:面向对象(一)的更多相关文章

  1. 我的Python升级打怪之路【六】:面向对象(二)

    面向对象的一些相关知识点 一.isinstance(obj,cls) 检查实例obj是否是类cls的对象 class Foo(object): pass obj = Foo() isinstance( ...

  2. 我的Python升级打怪之路【五】:Python模块

    模块,是一些代码实现了某个功能的集合 模块的分类: 自定义模块 第三方模块 内置模块 导入模块 import module from module.xx.xx import xx from modul ...

  3. 我的Python升级打怪之路【一】:python的简单认识

    Python的简介 Python与其他语言的对比: C和Python.Java.C# C语言:代码直接编译成了机器码,在处理器上直接执行 Python.Java.C#:编译得到相应的字节码,虚拟机执行 ...

  4. 我的Python升级打怪之路【七】:网络编程

    Socket网络套接字 socket通常也称为"套接字",用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通过”套接字“向网络发出请求或者应答网络请求. socket起源于 ...

  5. 我的Python升级打怪之路【四】:Python之前的一些补充

    字符串的格式化 1.百分号的方式 %[(name)][flags][width].[precision]typecode (name) 可选,用于选择指定的key flags 可选,可供选择的值有: ...

  6. 我的Python升级打怪之路【三】:Python函数

    函数 在函数之前,我们一直遵循者:面向过程编程,即:根据业务逻辑从上到下实现功能,开发过程中最常见的就是粘贴复制.代码就没有重复利用率. 例如:有好多的重复的代码 if 条件: 发送指令 接收结果 e ...

  7. 我的Python升级打怪之路【二】:Python的基本数据类型及操作

    基本数据类型 1.数字 int(整型) 在32位机器上,整数的位数是32位,取值范围是-2**31~2--31-1 在64位系统上,整数的位数是64位,取值范围是-2**63~2**63-1 clas ...

  8. gitlab 迁移、升级打怪之路:8.8.5--> 8.10.8 --> 8.17.8 --> 9.5.9 --> 10.1.4 --> 10.2.5

    gitlab 迁移.升级打怪之路:8.8.5--> 8.10.8 --> 8.17.8 --> 9.5.9 --> 10.1.4 --> 10.2.5 gitlab 数据 ...

  9. Python学习札记(三十六) 面向对象编程 Object Oriented Program 7 __slots__

    参考:slots NOTE 1.动态语言灵活绑定属性及方法. #!/usr/bin/env python3 class MyClass(object): def __init__(self): pas ...

随机推荐

  1. Ubuntu解压缩zip,tar,tar.gz,tar.bz2【转】

    ZIP zip可能是目前使用得最多的文档压缩格式.它最大的优点就是在不同的操作系统平台,比如Linux, Windows以及Mac OS,上使用.缺点就是支持的压缩率不是很高,而tar.gz和tar. ...

  2. linux 进阶2--C++读取lua文件中的变量、一维表、二维表

    lua 语言非常灵活,一般把lua 作为脚本文件,会用C++与之进行交互.最重要的是C++代码能读取到脚本中的变量.一维表.二维表. 这样有些参数就可以在lua文件进行更改,而不用重新更改C++代码. ...

  3. Android 使用pk10系统架设RecyclerView实现轮播图

    一.需求 ViewPager有个天生的缺陷是View无法重用,此外pk10系统架设详情咨询[企娥166848365]ViewPager的滑动过程会频繁requestLayout,尽管可以通过addVi ...

  4. ArcGIS Runtime SDK for Android 授权(arcgis android 去除水印)

    ArcGIS Runtime SDK for Android 授权 ESRI中国北京 要下载和安装 ArcGIS Runtime SDK for Android,您需要注册开发者账户,进而便拥有了访问 ...

  5. SSL证书 .pem转.pfx

    使用OpenSSL来进行转换 OpenSSL官网没有提供windows版本的安装包,可以选择其他开源平台提供的工具.例如 http://slproweb.com/products/Win32OpenS ...

  6. redis详细说明

    # By default Redis does not run as a daemon. Use 'yes' if you need it.# Note that Redis will write a ...

  7. OI数学 简单学习笔记

    基本上只是整理了一下框架,具体的学习给出了个人认为比较好的博客的链接. PART1 数论部分 最大公约数 对于正整数x,y,最大的能同时整除它们的数称为最大公约数 常用的:\(lcm(x,y)=xy\ ...

  8. “全栈2019”Java第八章:IntelliJ IDEA设置注释不显示在行首

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  9. linux性能评估与分析工具---CPU篇

    一. uptime root@calm:~# uptime :: up days, :, user, load average: 0.04, 0.09, 0.04 这里主要关注load average ...

  10. UIViewController读书笔记

    当一个VC把另一个VC作为子view加到自己的view时,一定要先调用addChildViewController(_:)方法. 因为一个VC的root view,也就是VC的view只能被这个VC持 ...