前面一系列博文讲解的都是面向过程的编程,如今是时候来一波面向对象的讲解了

一、简介

  面向对象编程是一种编程方式,使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。类就是一个模板,模板里可以包含多个方法(函数),方法里实现各种各样的功能,,对象则是根据模板创建的实例,通过实例,对象可以执行类中的方法,每个对象都拥有相同的方法,但各自的数据可能不同。

二、类、对象和方法

  在Python中,定义类是通过class关键字,class后面紧接着是类名,类名通常是大写开头的单词,紧接着是('要继承的类名'),表示该类是从哪个类继承下来的,通常如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类,也可以不写。

class MyClass():             # 创建类
def func(self): # 定义方法
pass obj1=MyClass() # 根据MyClass创建对象

  类中定义方法的时候,和以前定义函数一样,都是用来实现某种功能的,不过定义方法的时候必须传至少一个参数(self),代表创建的对象,而函数则不需要,下面来看一下self具体指的什么。

class MyClass():
def func(self,str):
print(self,str) def func1(self,str):
pass
obj1=MyClass() # self 哪个对象调用的,self就是那个对象 obi <====> self
print(obj1) # <__main__.MyClass object at 0x000002C2119AD4A8>
obj1.func('jason') # <__main__.MyClass object at 0x000002C2119AD4A8>

从上面打印出的数据可以看出,创建出来的obj1对象的地址值和func方法里面self的地址值一样,两者一致,说明self就是指代创建的对象,这里读者一定需要理解self指的是什么。

三、面向对象三大特性,封装、继承和多态。  

1、封装

  面向对象有3大特性,首先我们来说第一个特性,封装,封装一般是通过在类中封装数据,而通过对象或者self获取。和其他面向对象的语言类似,也是通过构造函数来进行数据封装。下面来看一下代码。 

class A:
def __init__(self,name): # 构造函数,初始化数据,
self.name=name # 封装数据 def f1(self):
print(self.name) # 通过self获取封装的数据 a=A('jason')
a.f1() #通过对象获取封装数据

还有一种封装的方式,使用私用的属性来封装数据,看一下具体的用法,

class A:
name='Jason'
__age=18 # 私有静态字段,
def __init__(self):
self.__like='soccer' # 私有普通字段
self.hobby='kkkk' def f1(self):
print(self.__age) # 私有静态字段,私有普通字段只能被类中的方法调用
print(self.__like)
# A.__age # 外部获取不到私有静态字段,数据被封装起来
a=A() # soccer
a.f1() # 18
print(a.hobby)

2、继承  

  Python里面的继承可以多继承,通过继承,可以获得父类的功能,继承的时候,如果父类中有重复的方法,优先找自己,如果有下面关系,D继承B,E继承C,F继承D,E,则查找顺序,D->B->E->C,如果有下面关系,B继承A,C继承A,D继承B,E继承C,F继承D,E,则查找顺序,D->B->E->C-A,下面来看一下用法

class A:
def f(self):
print('a')
class B:
def f(self):
print('b') def f1(self):
print('bbbb') class C(A,B):
def f1(self):
print('c')
cc=C()
cc.f() # a
cc.f1() # c #下面的是难点重点
class A:
def bar(self):
print('bar')
self.f1() class B(A):
def f1(self):
print('b') class C():
def f1(self):
print('c')
class D(B):
def f1(self):
print('d') class E(C,D):
pass d=D()
d.bar() # d.bar().f1() 去哪里找f1?

除了继承方法,还可以继承父类的构造函数  

# 继承构造方法
class A:
def __init__(self):
self.name='jason' class B(A):
def __init__(self):
self.age='16'
super(B,self).__init__()
# A.__init__(self) #另一种继承构造函数的方法 d=B()

3、多态     

 python本身就是支持多态的,所以在Python面向对象里面讨论多态并没有什么意义,这里也就不多讲了

四、类成员

  类的成员可以分为三大类:字段、方法和属性,下面从代码来看一下用法

class A:

    country = 'China'               # 静态字段,保存在类中 节省内存

    def __init__(self, name):
self.name = name # 普通字段,保存在对象中 def show(self): # 普通方法,保存在类中,对象通过类对象指针使用
print('普通方法')
return self.name @staticmethod # 静态方法,保存在类中
def f1(): # 没有 self 参数
print('静态方法') @classmethod # 类方法 相当于静态方法加了一个类名参数
def f2(cls): # 将当前类名当作参数传进去
print('类方法') @property # 属性或者叫特性,将普通方法伪装成字段,但是不能传入第二个参数
def f3(self):
print('属性')
return self.name @f3.setter # 设置属性
def f3(self,value):
print(value)
self.name= value a = A('jason')
print(hasattr(A, 'name')) # False
print(hasattr(a, 'name')) # True
print(hasattr(a, 'show')) # True
print(hasattr(A, 'country')) # True
print(hasattr(a, 'country')) # True # 优先,自己先去访问自己的成员除了类中的方法 # 用下面的方式去操作
# 通过类去访问的有:静态字段,静态方法,类方法(静态方法加一个类名参数)
# 通过对象访问的有:普通字段,普通方法
# 有 self,对象调用
# 无 self,类调用 A.f1() # 静态方法
a.show() # 普通方法
A.f2() # 类方法
a.f3 # 属性,获取属性值 执行的时候不用加括号,和对象调用普通字段类似
a.f3='kobe' # 设置属性值, print(A.country)
print(a.name) # 私有的只能在类中调用,不能再外部调用
class A:
name='Jason'
__age=18 # 私有静态字段
def __init__(self):
self.__like='soccer' # 私有普通字段
self.hobby='kkkk' def f1(self):
print(self.__age) # 私有静态字段,私有普通字段只能被类中的方法调用
print(self.__like)
# A.__age # 外部获取不到私有静态字段
a=A() # soccer
a.f1()

特殊成员,也不多说了,直接上代码了

class A:
def __init__(self):
print('init')
self.name='jason' def __str__(self):
return '........' def __call__(self, *args, **kwargs):
print('call')
return 1 def __getitem__(self, item):
print(item,type(item)) def __setitem__(self, key, value):
print(key,value) def __delitem__(self, key):
print(key) def __iter__(self):
yield 1
yield 2 a=A() # init 类后面加个括号执行 __init__
a() # call 对象后面加个括号执行 __call__
print(a) # 执行 __str__方法
k=A()() # init call k=1 a['dddd'] # 执行 __getitem__ <class 'str'>
a['name']='jason' # 执行 __setitem__
del a['name'] # 执行 __delitem__ a[1:3] # py2__getslice__,py3执行 __getitem__ <class 'slice'>
a[1:3]=[1,2] # py2__setslice__,py3执行 __setitem__
del a[1:3] # py2__detslice__,py3执行 __delitem__ print(a.__dict__) # {'name': 'jason'} 对象里面的字段
print(A.__dict__) # 类里面的字段 for i in a: # 执行__iter__
print(i)

五、元类  

  在Python中一切事物都是对象,类也是对象,那么问题来了,类是如何创建的了,类的定义是运行时动态创建的,而创建class的方法就是使用type()函数,通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。type可以接受一个类的描述作为参数,然后返回一个类可以像这样使用:type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

# 通过type自定义类
def f1(self):
print(self.name) MyClass=type('MyClass',(),{'name':'jason','f1':f1})
print(MyClass,type(MyClass)) # <class '__main__.MyClass'> ,<class 'type'>
print(MyClass.name) # jason my=MyClass()
print(my.__class__) # <class '__main__.MyClass'>
print(my) # <__main__.MyClass object at 0x000001F3013751D0>
my.f1() # jason

说完了type,那么type和元类到底是什么关系了,type实际上是一个元类。type就是Python在背后用来创建所有类的元类,除了使用type()动态创建类以外,要控制类的创建行为就要用到metaclass(元类),我们想创建出类,那就根据metaclass创建出类,所以:先定义metaclass,然后创建类。连接起来就是:先定义metaclass,就可以创建类,最后创建实例。元类的一般作用是拦截类的创建,修改类, 返回修改之后的类。下面看一下代码的使用

# 用函数实现
# def upper_attr(future_class_name, future_class_parents, future_class_attrs):
# attrs=((name, value) for name,value in future_class_attrs.items() if not name.startswith('__'))
# uppercase_attr=dict((name.upper(), value) for name,value in attrs)
# return type(future_class_name,future_class_parents,uppercase_attr())
#
# class A():
# # __metaclass__ = upper_attr
# country="China"
# def __init__(self):
# self.name='jason'
#
# print(hasattr(A,'country'))
# print(hasattr(A,'COUNTRY')) '''
__new__()方法接收到的参数依次是:
当前准备创建的类的对象;
类的名字;
类继承的父类集合;
类的方法集合。 __new__ 是在__init__之前被调用的特殊方法
__new__是用来创建对象并返回之的方法
而__init__只是用来将传入的参数初始化给对象
你很少用到__new__,除非你希望能够控制对象的创建
这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
如果你希望的话,你也可以在__init__中做些事情
还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用 ''' class UppMetaclass(type):
def __new__(cls, name,base,attrs):
uppattrs = dict((k.upper(),v)for k,v in attrs.items() if not k.startswith('__'))
# return type.__new__(cls, name, base, uppattrs)
return super(UppMetaclass, cls).__new__(cls, name, base, uppattrs) class A(metaclass=UppMetaclass):
# __metaclass__ = UppMetaclass
country="China"
def __init__(self):
self.name='jason' print(hasattr(A,'country'))
print(hasattr(A,'COUNTRY'))
print(A.COUNTRY)

六、单例模式 

 单例,顾名思义单个实例,单利模式存在的目的是保证当前内存中仅存在单个实例,避免内存浪费,下面直接看一下怎么实现单例

class Instance:                    # 方法一

    __instance=None                               # 私有静态字段

    def __init__(self):
self.name='jason'
self.passwd='kkkkk' @staticmethod
def get_instance():
if not Instance.__instance: # 如果私有静态字段为空的话,创建一个实例
Instance.__instance = Instance()
return Instance.__instance # 不为空的话,直接返回一个实例 obj1=Instance.get_instance() # 执行静态方法,创建一个实例赋值给obj1
obj2=Instance.get_instance() # 执行静态方法,将私有静态字段保存的对象赋值给obj2
print(obj1) # <__main__.Instance object at 0x0000021AEDF56048>
print(obj2) # <__main__.Instance object at 0x0000021AEDF56048> class Singleton():              # 方法二
def __new__(cls, *args, **kwargs):
if not hasattr(cls,'_instance'):
cls._instance=super(Singleton,cls).__new__(cls,*args, **kwargs)
return cls._instance class A(Singleton):
pass
a=A()
b=A()
print(a is b) class Singleton(type):                # 方法三
def __call__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instance class A(metaclass=Singleton):    
# __metaclass__ = Singleton
pass
a = A()
b = A()
print(a is b)

七、补充 

属性的另一种表达方式:

class A:
def __init__(self):
self.mylist = [1, 2, 3, 4] def getmylist(self):
print(self.mylist) def setmylist(self, value):
self.mylist.append(value)
print(self.mylist)
def delmylist(self):
if len(self.mylist) > 0:
self.mylist.pop()
print(self.mylist)
my_pro_list = property(getmylist, setmylist, delmylist, 'Property') a = A()
a.my_pro_list
a.my_pro_list = 7
del a.my_pro_list '''
[1, 2, 3, 4]
[1, 2, 3, 4, 7]
[1, 2, 3, 4]
'''

单利补充:

class SingMetaclass(type):
def __call__(self, *args, **kwargs):
if not hasattr(self, 'instance'):
# self.instance = super(SingMetaclass, cls).__new__(cls, name, base, attrs)
# self.instance = self.__new__(self, *args, **kwargs) # 下面这种方式也行,但是没有注释掉的好理解
self.instance = super().__call__(*args, **kwargs)
return self.instance class A(metaclass=SingMetaclass):
pass

py27继承顺序,有一种情况和py3不一样,如下图,其中bar没有继承object(经典类)、深度优先,其他情况一样,都是广度优先

  

# py27

class bar():
def f1(self):
print('bar') class A(bar):
def f(self):
print('a') class C(A):
def f(self):
print('c') class B(bar):
def f1(self):
print('b') class D(B):
def f1(self):
print('d') class E(C,D):
def f(self):
print('e') e = E()
e.f1()

抽象类和抽象方法:

from abc import ABCMeta

from abc import abstractmethod

class Absclass(metaclass=ABCMeta):      # 抽象类+抽象方法,类似接口,约束类
@abstractmethod
def f1(self): pass @abstractmethod
def f2(self): pass class B(Absclass):        # B 需要完全实现抽象类中的抽象方法
def f1(self): pass def f2(self): pass # 如果 B 类中没有完全实现 Absclass 类中的方法话,可以编译过,但是执行的话会报错 def f3(self): pass c = B()

  

  

  

Python全栈开发之9、面向对象、元类以及单例的更多相关文章

  1. 战争热诚的python全栈开发之路

    从学习python开始,一直是自己摸索,但是时间不等人啊,所以自己为了节省时间,决定报个班系统学习,下面整理的文章都是自己学习后,认为重要的需要弄懂的知识点,做出链接,一方面是为了自己找的话方便,一方 ...

  2. python全栈开发之OS模块的总结

    OS模块 1. os.name()      获取当前的系统 2.os.getcwd      #获取当前的工作目录 import os cwd=os.getcwd() # dir=os.listdi ...

  3. Python全栈开发之6、面向对象

    一.创建类和对象 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中) 对象,根据模板创建的实例 ...

  4. Python全栈开发之MySQL(二)------navicate和python操作MySQL

    一:Navicate的安装 1.什么是navicate? Navicat是一套快速.可靠并价格相宜的数据库管理工具,专为简化数据库的管理及降低系统管理成本而设.它的设计符合数据库管理员.开发人员及中小 ...

  5. Python全栈开发之14、Javascript

    一.简介 前面我们学习了html和css,但是我们写的网页不能动起来,如果我们需要网页出现各种效果,那么我们就要学习一门新的语言了,那就是JavaScript,JavaScript是世界上最流行的脚本 ...

  6. Python全栈开发之1、输入输出与流程控制

    Python简介 python是吉多·范罗苏姆发明的一种面向对象的脚本语言,可能有些人不知道面向对象和脚本具体是什么意思,但是对于一个初学者来说,现在并不需要明白.大家都知道,当下全栈工程师的概念很火 ...

  7. python全栈开发之路

    一.Python基础 python简介 python数据类型(数字\字符串\列表) python数据类型(元组\字典) python数据类型(集合) python占位符%s,%d,%r,%f prin ...

  8. Python全栈开发之7、面向对象编程进阶-类属性和方法、异常处理和反射

    一.类的属性 1.@property属性 作用就是通过@property把一个方法变成一个静态属性 class Room: def __init__(self,name,length,width,he ...

  9. Python全栈开发之21、django

    http://www.cnblogs.com/wupeiqi/articles/5237704.html http://www.cnblogs.com/wupeiqi/articles/5246483 ...

随机推荐

  1. springMVC的controller返回值

    1.可以返回ModelAndView 2.可以返回一个String字符串:即一个jsp页面的逻辑视图名,这个在springMVC.xml中可以配置此页面逻辑视图的前缀和后缀 3.可以返回void类型: ...

  2. windows环境libevent搭建和demo分析

    libevent框架之前有做过分析,这次是谈谈如何将libevent搭建在vs工作环境下, 并且编写一个demo进行测试.测试过程中会再一次带大家分析消息是怎么传递 的. 我的libevent版本li ...

  3. Jenkins CI Pipeline scripting

    Jenkins pipeline is a suite of Jenkins plugins. Pipelines can be seen as a sequence of stages to per ...

  4. Splay 区间操作(二)

    首先基本操作如下: 删除第rank个点 void Remove(int id){//删除第rank个点 rank++; int x = find(root, rank - 1); splay(x, 0 ...

  5. JNI实现JAVA和C++互相调用

    SDK.h #ifndef SDK_H #define SDK_H #include "AsyncProxy.h" #include "Module.h" #i ...

  6. Excel 报表导入导出

    使用 Excel 进行报表的导入导出,首先下载相关的 jar 和 excel util. Excel Util 下载地址 引入依赖: <!-- poi office --> <dep ...

  7. HDU 1695 容斥

    又是求gcd=k的题,稍微有点不同的是,(i,j)有偏序关系,直接分块好像会出现问题,还好数据规模很小,直接暴力求就行了. /** @Date : 2017-09-15 18:21:35 * @Fil ...

  8. 使用JavaScript实现使用鼠标画线的效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. 第七周 ch04 课下测试补交

    2017-2018-1 20155335 <信息安全系统设计基础>第7周 课下测试博客 本人不慎忘记去交dao'zhi 测试题目: SEQ+对SEQ的改变有() A . PC的计算挪到取指 ...

  10. XML & JSON---iOS-Apple苹果官方文档翻译

      技术博客http://www.cnblogs.com/ChenYilong/   新浪微博http://weibo.com/luohanchenyilong   //转载请注明出处--本文永久链接 ...