python基础-第七篇-7.2面向对象(进阶篇)
进入到今天的探索前,我先对上节内容进行一下回顾:
- 面向对象是一种编程方式,此编程方式的实现是基于对类和对象的使用
- 类是一个模板,模板中包装了多个函数可供使用
- 对象是基于类创建的,实例用于调用被包装在类中的函数
- 对于c#和java--只能用面向对象编程,而对于ruby和python则可以用函数编程和面向对象编程
- 面向对象不是所有的情况都适用,当两个东西不存在公共的部分,最好用函数编程
- 对象编程里的self是形式参数,是python内部把对象当参数传递进去的
- __init__ 构造方法--当实例化对象时,会自动执行类里的这个方法,并在此方法里执行具体的封装操作
- __del__ 解释器销毁对象的时候自动调用的方法,叫析构方法
类的成员
类的成员可以分为三大类:字段、方法和属性
注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
一、字段
字段分普通字段和静态字段
另外它们在内存上也有所区别
- 在前面我们知道,实例化对象的过程其实也是对象带他的普通字段再开辟一份内存空间
- 静态字段属于类,而普通字段属于对象
那现在我们来看看静态字段在类调用和对象调用过程中的内存变化--
class schoolmember: human_num = 10000 def __init__(self,name,age,salary):
self.name = name
self.age = age
self.salary = salary def sleep(self):
print('%s go to sleep....'%self.name) obj1 = schoolmember('小明',22,10)
obj2 = schoolmember('小红',56,10) obj1.salary += 1000
obj2.salary += 2000
print(obj1.salary) #
print(obj2.salary) #
print('*'*20) obj1.human_num += 3000
obj2.human_num += 4000
print(schoolmember.human_num) #
print(obj1.human_num) #
print(obj2.human_num) #
print('*'*20) schoolmember.human_num += 5000
print(schoolmember.human_num) #
print(obj1.human_num) #
print(obj2.human_num) #
我们从内存的角度来看这个问题,当我们使用类名.字段名去调用静态变量的时候,这个变量是存在类的内存中的,这个时候各个对象都可以取到类的内存中的变 量,但是当我们使用对象名.字段名去修改这个静态变量的时候,我们其实是在对象的内存中又开辟了一块空间去存储这个字段和修改后的值,所以一个对象的字段 发生改变,类和其他对象的该字段并不会发生变化。哎呦,总之解决这个问题的最终办法就是始终用 类名.字段名 去使用类中的静态变量就对了!
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
二、方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类调用;无默认参数,可以不用创建就能实现某个功能,等同在类中实现函数式编程
class Cat: def __init__(self,name):
self.name = name #普通方法:至少有一个self参数
def eat(self):
print('%s is eating...'%self.name) #静态方法,无默认参数
@staticmethod
def drink(something,obj): #可以把对象当做普通参数传入
print('%s is drinking %s.'%(obj.name,something)) #类方法,至少有一个cls参数
@classmethod
def sleep(cls,obj):
print('%s is sleeping...'%obj.name) ca = Cat('小花猫')
ca.eat()
Cat.drink('water',ca)
Cat.sleep(ca)
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
三、属性
特性:普通方法的变种,将方法伪造成一种字段,定义时就self参数,执行的结果为return返回值
# ############### 定义 ###############
class Foo: def func(self):
pass # 定义属性
@property
def prop(self):
pass
# ############### 调用 ###############
foo_obj = Foo() foo_obj.func()
foo_obj.prop #调用属性
定义和调用
由属性的定义和调用要注意一下几点:
- 定义时,在普通方法的基础上添加 @property 装饰器;
- 定义时,属性仅有一个self参数
- 调用时,无需括号
方法:foo_obj.func()
属性:foo_obj.prop
注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象
属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。
实例:对于主机列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据(即:limit m,n),这个分页的功能包括:
- 根据用户请求的当前页和总数据条数计算出 m 和 n
- 根据m 和 n 去数据库中请求数据
# ############### 定义 ###############
class Pager: def __init__(self, current_page):
# 用户当前请求的页码(第一页、第二页...)
self.current_page = current_page
# 每页默认显示10条数据
self.per_items = 10 @property
def start(self):
val = (self.current_page - 1) * self.per_items
return val @property
def end(self):
val = self.current_page * self.per_items
return val # ############### 调用 ############### p = Pager(1)
p.start 就是起始值,即:m
p.end 就是结束值,即:n
从上述可见,Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。
类成员的修饰符
刚才我们讲到类的成员大体还可以分为这么两类
- 公有成员,在任何地方都能访问
- 私有成员,只有在类的内部才能方法,私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)
class Animal: __ke = '属' def __init__(self,name,sex):
self.__name = name
self.__sex = sex def get_putongziduan(self):
print(self.__name,self.__sex) def __eat(self):
print('eat') def __drink(self):
print('drink') def get_putongfangfa(self):
self.__drink()
self.__eat() def get_jingtaiziduan(self):
print(self.__ke) def sleep(self):
print('sleep') class Cat(Animal): def jiao(self):
print('jiao') def get_info(self):
print(self.__name)
print(self.__ke)
self.__drink() #子类创建的对象
cat = Cat('小白猫','male')
#父类创建的对象
animal = Animal('小花狗','man') #父类外部调用私有普通字段
# print(animal.__name)
#子类外部外部调用私有普通字段
# print(cat.__name) #父类外部调用私有方法
# animal.__drink()
#子类外部调用私有方法
# cat.__drink() #父类内部部调用私有普通字段
animal.get_putongziduan()
cat.get_putongziduan() #子类内部调用私有字段和私有方法
# cat.get_info()
结论:私有的只有自己能用,就算你儿子,你孙子来了也用不了
类的特殊成员
- __doc__ 表示类的描述信息
class Foo:
""" 描述类信息,这是用于看片的神奇 """ def func(self):
pass print Foo.__doc__
#输出:类的描述信息
- __module__ 表示当前操作的对象在哪个模块
- __class__ 表示当前操作的对象的类是什么
#!/usr/bin/env python
# -*- coding:utf-8 -*- class C: def __init__(self):
self.name = 'wupeiqi'
lib.aa.py
from lib.aa import C obj = C()
print obj.__module__ # 输出 lib.aa,即:输出模块
print obj.__class__ # 输出 lib.aa.C,即:输出类
index.py
- __init__ 构造方法,通过创建对象时,自动执行
- __del__ 析构方法,当对象在内存中被释放时,自动执行
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
- __call__ 对象后面加括号,触发执行 --对象()或类()()
class Foo: def __init__(self):
pass def __call__(self, *args, **kwargs): print '__call__' obj = Foo() # 执行 __init__
obj() # 执行 __call__
- __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'}
- __str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值
class Foo: def __str__(self):
return 'wupeiqi' obj = Foo()
print obj
# 输出:wupeiqi
- __getitem__、__setitem__、__delitem__ 用于索引操作,如字典。分别表示获取、设置、删除数据
#!/usr/bin/env python
# -*- coding:utf-8 -*- class Foo(object): def __getitem__(self, key):
print '__getitem__',key def __setitem__(self, key, value):
print '__setitem__',key,value def __delitem__(self, key):
print '__delitem__',key obj = Foo() result = obj['k1'] # 自动触发执行 __getitem__
obj['k2'] = 'wupeiqi' # 自动触发执行 __setitem__
del obj['k1'] # 自动触发执行 __delitem__
- __getslice__、__setslice__、__delslice__ 该三个方法用于分片操作,如:列表
#!/usr/bin/env python
# -*- coding:utf-8 -*- class Foo(object): def __getslice__(self, i, j):
print '__getslice__',i,j def __setslice__(self, i, j, sequence):
print '__setslice__',i,j def __delslice__(self, i, j):
print '__delslice__',i,j obj = Foo() obj[-1:1] # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__
del obj[0:2] # 自动触发执行 __delslice__
- __enter__、__exit__ 上下文管理时,一进一出
import pymysql class DBConnection(object): def __init__(self, host, port, username, password, db_name):
self.host = host
self.port = port
self.user = username
self.passwd = password
self.db = db_name def __enter__(self):
print(self.__dict__)
self.conn = pymysql.connect(**self.__dict__)
# self.conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='testdb')
self.cursor = self.conn.cursor()
return self def __exit__(self, exc_type, exc_val, exc_tb):
self.cursor.close()
self.conn.close() def fetch_all(self):
self.cursor.execute("select * from app01_token")
rows = self.cursor.fetchall()
print(rows) with DBConnection('127.0.0.1',3306,'root','123456','testdb') as db:
db.fetch_all()
- __getattr__, __setattr__ 支持属性访问,和属性赋值
class Foo: def __getattr__(self, item):
print(item) def __setattr__(self, key, value):
print(key, value) obj = Foo()
obj.xx = 123
obj.xx
- __iter__ 用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__
class Foo(object):
pass obj = Foo() for i in obj:
print i # 报错:TypeError: 'Foo' object is not iterable
class Foo(object): def __iter__(self):
pass obj = Foo() for i in obj:
print i # 报错:TypeError: iter() returned non-iterator of type 'NoneType'
class Foo(object): def __init__(self, sq):
self.sq = sq def __iter__(self):
return iter(self.sq) obj = Foo([11,22,33,44]) for i in obj:
print i
- __new__和__metaclass__
class Foo(object): def __init__(self):
pass obj = Foo() # obj是通过Foo类实例化的对象
上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。
print type(obj) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
print type(Foo) # 输出:<type 'type'> 表示,Foo类对象由 type 类创建
所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
那么,创建类就可以有两种方式:
a). 普通方式
class Foo(object): def func(self):
print 'hello wupeiqi'
b).特殊方式(type类的构造函数)
def func(self):
print 'hello wupeiqi' Foo = type('Foo',(object,), {'func': func})
#type第一个参数:类名
#type第二个参数:当前类的基类
#type第三个参数:类的成员
==》 类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
class MyType(type): def __init__(self, what, bases=None, dict=None):
super(MyType, self).__init__(what, bases, dict) def __call__(self, *args, **kwargs):
obj = self.__new__(self, *args, **kwargs) self.__init__(obj) class Foo(object): __metaclass__ = MyType def __init__(self, name):
self.name = name def __new__(cls, *args, **kwargs):
return object.__new__(cls, *args, **kwargs) # 第一阶段:解释器从上到下执行代码创建Foo类
# 第二阶段:通过Foo类创建obj对象
obj = Foo()
静态字段和类对象指针
本人搔师博客文章
欢迎大家对我的博客内容提出质疑和提问!谢谢
笔者:拍省先生
python基础-第七篇-7.2面向对象(进阶篇)的更多相关文章
- Python开发【第七篇】:面向对象 和 python面向对象进阶篇(下)
Python开发[第七篇]:面向对象 详见:<Python之路[第五篇]:面向对象及相关> python 面向对象(进阶篇) 上一篇<Python 面向对象(初级篇)> ...
- C#基础第七天-作业-利用面向对象的思想去实现名片-动态添加
1.利用面向对象的思想去实现: (增加,修改,删除,查询,查询全部)需求:根据人名去(删除/查询).指定列:姓名,年龄,性别,爱好,电话. 多条添加 , 动态添加 名片 本系列教程: C#基础总结之八 ...
- python基础-第七篇-7.1初识类和对象
创建类和对象 刚开始我们接触得多的编程方式为面向过程编程,这种方式就是根据业务逻辑从上往下垒代码,后来又出现了函数式编程,就是为了提高代码的重用性,减轻程序猿的工作量--而今天我们即将学的 面向对象编 ...
- python基础-第七篇-7.4异常处理
异常基础: 异常处理首先要捕获异常,不让程序中断,也不让错误信息直接呈现出来,然后就是你该怎么处理异常,以什么方式显示 try: pass except Exception,ex: pass 在需要用 ...
- python基础-第七篇-7.3反射
定义 反射是根据字符串的形式去对操作其成员 了解反射前,我先看看内置方法__import__,还记得这个内置方法吗? __import__ 用于以字符串的形式导入模块 inp = input('请输 ...
- Python 面向对象 (进阶篇)
<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可 ...
- Python基础(七)内置函数
今天来介绍一下Python解释器包含的一系列的内置函数,下面表格按字母顺序列出了内置函数: 下面就一一介绍一下内置函数的用法: 1.abs() 返回一个数值的绝对值,可以是整数或浮点数等. 1 2 3 ...
- python 零散记录(七)(上) 面向对象 类 类的私有化
python面向对象的三大特性: 多态,封装,继承 多态: 在不知道对象到底是什么类型.又想对其做一些操作时,就会用到多态 如 'abc'.count('a') #对字符串使用count函数返回a的数 ...
- python之路 面向对象进阶篇
一.字段 字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同, 普通字段属于对象 静态字段属于类 class Province: # 静态字段 countr ...
随机推荐
- Mockjs生成Vue数据表格
1.npm install mockjs --save 2.在src文件下建mock.js文件 3.mock.js文件文件内容 //引入mockjs import Mock from 'mockjs' ...
- 跟着百度学习之ThinkPHP的认识/初窥
MVC全称(Model View Controller) Model:模型(可以理解位数据库操作模型) View:视图(视图显示) Controller:(控制器) 简单的说框架就是一个类的集合.集合 ...
- eclipse开发cocos2dx 3.2环境搭建之中的一个: Android C\C++环境搭建(ndk r9d)
这几天有时间,琢磨一下cocos2dx.cocos2d家族事实上挺庞大的.也有cocos2d-android这样的能够直接用Java语言来开发的,可是cocos2d-android资料相对少一些.并且 ...
- 【Hadoop】HA 场景下访问 HDFS JAVA API Client
客户端需要指定ns名称,节点配置,ConfiguredFailoverProxyProvider等信息. 代码示例: package cn.itacst.hadoop.hdfs; import jav ...
- [转]Vim插件管理工具Vundle
原文:http://www.linuxzen.com/vimpei-zhi-xi-lie-cha-jian-guan-li.html 当转载成为一种习惯.. 最近对Vim进行了一番较大的配置变动,所以 ...
- Golang 中操作 Mongo Update 的方法
Golang 和 MongoDB 中的 ISODate 时间交互问题 2018年02月27日 11:28:43 独一无二的小个性 阅读数:357 标签: GolangMongoDB时间交互时间转换 更 ...
- php 返回上一页并刷新
echo "<script>alert('分组已存在!');location.href='".$_SERVER["HTTP_REFERER"].&q ...
- 【BZOJ】1648: [Usaco2006 Dec]Cow Picnic 奶牛野餐(dfs)
http://www.lydsy.com/JudgeOnline/problem.php?id=1648 水题.. dfs记录能到达的就行了.. #include <cstdio> #in ...
- jQuery实现淡入淡出二级下拉导航菜单的方法
本文实例讲述了jQuery实现淡入淡出二级下拉导航菜单的方法.分享给大家供大家参考.具体如下: 这是一款基于jQuery实现的导航菜单,淡入淡出二级的菜单导航,很经常见到的效果,这里使用的是jquer ...
- Laravel5.1 搭建博客 --编译前端文件
上篇文章写了Gulp编译前端文件,这篇记录下在搭建博客中使用Gulp 1 引入bootstrap和js 1.1 首先先在项目本地安装Bower sudo npm install bower 1.2 创 ...