#!/usr/bin/env python
# coding: utf-8
# # Table of Contents
#
# # Python 面向对象进阶
# ## 类成员
#
# 类的成员可以分为三大类:字段、方法和属性
# ![类成员信息](class_info.png)
# 注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份。
# ## 字段
#
# 字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,
#
# - 普通字段属于对象
# - 静态字段属于类
# In[1]:
class Province:
# 静态字段(属于类)
country = '中国'
def __init__(self, name):
# 普通字段(属于对象)
self.name = name
# 直接访问普通字段
obj = Province('湖南')
print(obj.name)
# 直接访问静态字段
Province.country
# 由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。其在内容的存储方式类似如下图:
# ![类字段](class_field.png)
# 由上图可是:
#
# - 静态字段在内存中只保存一份
# - 普通字段在每个对象中都要保存一份
#
# 应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
# ## 方法
#
# 方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
#
# - 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
# - 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
# - 静态方法:由类调用;无默认参数;
# In[ ]:
class Foo:
def normal_func(self):
""" 定义普通方法,至少有一个self参数 """
# print self.name
print('普通方法')
@classmethod
def class_func(cls):
""" 定义类方法,至少有一个cls参数 """
print('类方法')
@staticmethod
def static_func():
""" 定义静态方法 ,无默认参数"""
print('静态方法')
# 调用普通方法
f = Foo()
f.normal_func()
# 调用类方法
Foo.class_func()
# 调用静态方法
Foo.static_func()
# **相同点:** 对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
#
# **不同点:** 方法调用者不同、调用方法时自动传入的参数不同。
# ## 属性
#
# 如果你已经了解Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种,像使用字段那样简单的使用,但是又能像方法一样做一些运算。
#
# 对于属性,有以下两个知识点:
#
# - 属性的基本使用
# - 属性的两种定义方式
# **属性的基本使用:**
# In[2]:
# ############### 定义 ###############
class Foo:
def func(self):
return "normal"
# 定义属性
@property
def prop(self):
return "property"
# ############### 调用 ###############
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 去数据库中请求数据
# In[3]:
# ############### 定义 ###############
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的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。(比字段灵活)
# **属性的两种定义方式**
#
# 属性的定义有两种方式:
#
# - 装饰器 即:在方法上应用装饰器
# - 静态字段 即:在类中定义值为property对象的静态字段
# **装饰器方式:**在类的普通方法上应用 **@property** 装饰器:
# In[4]:
class Pager:
def __init__(self, all_count):
self.all_count = all_count
@property
def all_pager(self):
a1, a2 = divmod(self.all_count, 10)
if a2 == 0:
return a1
else:
return a1 + 1
@all_pager.setter
def all_pager(self, value):
self.all_count = value
@all_pager.deleter
def all_pager(self):
del self.all_count
print('del all_pager')
p = Pager(101)
ret = p.all_pager # 仿字段一样操作,获取值
print(ret)
print(p.all_pager)
p.all_pager = 121 # 仿字段一样操作,设置值
print(p.all_pager)
del p.all_pager # 仿字段一样操作,删除值
# **静态字段方式**: 创建值为property对象的静态字段
# In[5]:
class Foo:
def get_bar(self):
return 'Kevin'
BAR = property(get_bar)
obj = Foo()
reuslt = obj.BAR # 自动调用get_bar方法,并获取方法的返回值
print(reuslt)
# property的构造方法中有个四个参数
#
# - 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
# - 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
# - 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
# - 第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息
# In[6]:
help(property)
# In[7]:
class Foo:
def __init__(self):
self.value = None
def get_bar(self):
return self.value
def set_bar(self, value):
self.value = value
def del_bar(self):
del self.value
# property(fget=None, fset=None, fdel=None, doc=None)
BAR = property(get_bar, set_bar, del_bar, 'description...')
obj = Foo()
obj.BAR # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "liang" # 自动调用第二个参数中定义的方法:set_bar方法,并将“liang”当作参数传入
del obj.BAR # 自动调用第三个参数中定义的方法:del_bar方法
# 由于静态字段方式创建属性具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:**获取、修改、删除**
# In[8]:
class Goods(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
def get_price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
def set_price(self, value):
self.original_price = value
def del_price(self):
del self.original_price
PRICE = property(get_price, set_price, del_price, '价格属性描述...')
obj = Goods()
obj.PRICE = 200 # 修改商品原价
obj.PRICE # 获取商品价格
del obj.PRICE # 删除商品原价
# ## 类特殊成员
#
# 上文介绍了Python的类成员以及成员修饰符,从而了解到类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。无论人或事物往往都有不按套路出牌的情况,Python的类成员也是如此,存在着一些具有特殊含义的成员,详情如下:
# 1、 \__doc__
#
# 表示类的描述信息
# In[9]:
class Foo:
""" 描述类信息,这是用于看片的神器"""
def func(self):
pass
print(Foo.__doc__)
#输出:类的描述信息
# 2、 \__module\__ 和 \__class__
#
# - \__module__ 表示当前操作的对象在哪个模块
# - \__class__ 表示当前操作的对象的类是什么
# In[10]:
class C:
def __init__(self):
self.name = 'Kevin'
obj = C()
print(obj.__module__) # 输出 lib.aa,即:输出模块
print(obj.__class__) # 输出 lib.aa.C,即:输出类
# 3、 \__init__
#
# - 构造方法,通过类创建对象时,自动触发执行。
# In[11]:
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
obj = Foo('Kevin', 18) # 自动执行类中的 __init__ 方法
print(obj.name, obj.age)
# 4、 \__del__
#
# 析构方法,当对象在内存中被释放时,自动触发执行。
#
# 注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
# In[12]:
class Foo:
def __del__(self):
pass
# 5、 \__call__
#
# 对象后面加括号,触发执行。
#
# 注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
# In[13]:
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
# 6、 \__dict__
#
# 类或对象中的所有成员
#
# 上文中我们知道:类的普通字段属于对象;类中的静态字段和方法等属于类,即:
# ![类字段](class_field.png)
# In[14]:
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': , '__init__': , '__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__方法,那么在打印 对象 时,默认输出该方法的返回值。
# In[15]:
class Foo:
def __str__(self):
return 'There are some describings...'
obj = Foo()
print(obj)
# 8、\__getitem\__、\__setitem\__、\__delitem__
#
# 用于索引操作,如字典。以上分别表示获取、设置、删除数据
# In[16]:
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'] = 'Kevin' # 自动触发执行 __setitem__
del obj['k1'] # 自动触发执行 __delitem__
# 是不是感觉自己可以写一个字典的类了,接下来我们手动写一个有序得字典:
# In[17]:
class MyDict(dict): # 继承dict类
def __init__(self):
self.li = [] # 设置一个有序list
super(MyDict, self).__init__() # 调用基础类的构造(__init__)方法
def __setitem__(self, key, value):
self.li.append(key) # 每次创建key,value, 将key 加入到有序list
super(MyDict, self).__setitem__(key,value)
def __delitem__(self, key):
del self.li[self.li.index(key)]
super(MyDict, self).__delitem__(key)
def __str__(self):
dict_list = []
for key in self.li:
value = self.get(key)
dict_list.append("'%s':%s" % (key,value))
dict_str = "{" + ",".join(dict_list) + "}"
return dict_str
obj = MyDict()
obj['k1'] = 123
obj['k2'] = 456
del obj['k1']
print(obj)
# 9、 \__iter__
#
# 用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 \__iter__
# In[18]:
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)
# 以上步骤可以看出,for循环迭代的其实是 iter([11,22,33,44]) ,所以执行流程可以变更为:
#
# for 内部其实做的是:
# In[19]:
obj = iter([11,22,33,44])
while True:
val = obj.__next__()
print(val)
# 10、 \__new\__ 和 \__metaclass__
#
# 阅读以下代码:
# In[ ]:
class Foo(object):
def __init__(self):
pass
obj = Foo() # obj是通过Foo类实例化的对象
# 上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
#
# 如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。
# In[20]:
print(obj.__class__) # 输出: 表示,obj 对象由Foo类创建
print(Foo.__class__) # 输出: 表示,Foo类对象由 type 类创建
# In[21]:
age = 35
age.__class__
name = 'Kevin'
name.__class__
def foo(): pass
foo.__class__
class Bar(object): pass
b = Bar()
b.__class__
# In[22]:
age.__class__.__class__
name.__class__.__class__
foo.__class__.__class__
b.__class__.__class__
# 所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
#
# 那么,创建类就可以有两种方式:
# **a). 普通方式**
# In[ ]:
class Foo(object):
def func(self):
print('hello Kevin')
# **b).特殊方式(type类的构造函数)**
# In[23]:
def func(self):
print('hello Kevin')
Foo = type('Foo',(object,), {'func': func})
obj = Foo()
obj.func()
#type第一个参数:类名
#type第二个参数:当前类的基类
#type第三个参数:类的成员
# ** 类 是由 type 类实例化产生 **
#
# 那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
#
# 答:type 是一个元类,所有的类都是由这个元类来实例的,关于元类更多这里不涉及,感兴趣的可自行查找了解。
# ## 扩展
# ** 1、 isinstance **
#
# 检查对象是否是某个类实例出来的
# In[24]:
class Foo(object):
pass
obj = Foo()
isinstance(obj, Foo)
# ** 2、 issubclass **
#
# 检查某个类是不是这个另外一个类的子类
# In[25]:
class Foo(object):
pass
class Bar(Foo):
pass
# 检查Bar类是否是 Foo 类的派生类
issubclass(Bar, Foo)
# ## 异常处理
#
# 在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页!!!
# In[ ]:
try:
pass
except Exception,ex:
pass
# 需求:将用户输入的两个数字相加:
#
# 如果用户输入的有误,则提示用户
# In[26]:
while True:
num1 = input('num1:')
num2 = input('num2:')
try:
num1 = int(num1)
num2 = int(num2)
result = num1 + num2
except Exception as e:
print('出现异常,信息如下:')
print(e)
break
# python中的异常种类非常多,每个异常专门用于处理某一项异常!!!
#
# ### 常用异常:
#
# - AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
# - IOError 输入/输出异常;基本上是无法打开文件
# - ImportError 无法引入模块或包;基本上是路径问题或名称错误
# - IndentationError 语法错误(的子类) ;代码没有正确对齐
# - IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
# - KeyError 试图访问字典里不存在的键
# - KeyboardInterrupt Ctrl+C被按下
# - NameError 使用一个还未被赋予对象的变量
# - SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
# - TypeError 传入对象类型与要求的不符合
# - UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,导致你以为正在访问它
# - ValueError 传入一个调用者不期望的值,即使值的类型是正确的
# ### 其他异常:
# In[ ]:
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError
# 对于上述实例,异常类只能用来处理指定的异常情况,如果非指定异常则无法处理。
# In[27]:
# 未捕获到异常,程序直接报错
s1 = 'hello'
try:
int(s1)
except IndexError as e:
print(e)
# 所以,写程序时需要考虑到try代码块中可能出现的任意异常,可以这样写:
# In[28]:
s1 = 'hello'
try:
int(s1)
except IndexError as e:
print(e)
except KeyError as e:
print(e)
except ValueError as e:
print(e)
# 万能异常 在python的异常中,有一个万能异常:Exception,他可以捕获任意异常,即:
# In[29]:
s1 = 'hello'
try:
int(s1)
except Exception as e:
print(e)
# 接下来你可能要问了,既然有这个万能异常,其他异常是不是就可以忽略了!
#
# 答:当然不是,对于特殊处理或提醒的异常需要先定义,最后定义Exception来确保程序正常运行。
# In[30]:
s1 = 'hello'
try:
int(s1)
except KeyError as e:
print('键错误')
except IndexError as e:
print('索引错误')
except Exception as e:
print('错误')
# ### 异常处理结构:
# In[ ]:
try:
# 主代码块
pass
except KeyError as e:
# 异常时,执行该块
pass
else:
# 主代码块执行完,执行该块
pass
finally:
# 无论异常与否,最终执行该块
pass
# ### 主动触发异常:
# In[31]:
try:
raise Exception('错误了。。。')
except Exception as e:
print(e)
# ### 自定义异常:
# In[32]:
class MyException(Exception):
def __init__(self, msg):
self.message = msg
def __str__(self):
return self.message
try:
raise MyException('我的异常')
except MyException as error:
print(error)
# ### 断言:
# In[ ]:
assert 1 == 1 #如果条件满足就不报错,如果条件不满足就会报错
assert 1 == 2
# ## 反射
#
# python中的反射功能是由以下四个内置函数提供:** hasattr、getattr、setattr、delattr,** 这四个函数分别用于对对象内部执行:
# - 检查是否含有某成员
# - 获取成员
# - 设置成员
# - 删除成员。
# In[33]:
class Foo(object):
def __init__(self):
self.name = 'Kevin'
def func(self):
return 'func'
obj = Foo()
# #### 检查是否含有成员 ####
hasattr(obj, 'name')
hasattr(obj, 'func')
# #### 获取成员 ####
getattr(obj, 'name')
getattr(obj, 'func')
# #### 设置成员 ####
setattr(obj, 'age', 18)
setattr(obj, 'show', lambda num: num + 1)
# #### 删除成员 ####
delattr(obj, 'name')
# ## 设计模式
#
# ### 单例
#
# 单例,顾名思义单个实例。
# 学习单例之前,首先来回顾下面向对象的内容:
#
# python的面向对象由两个非常重要的两个“东西”组成:类、实例(对象)
# 如:创建对数据库操作的公共类
#
# - 增
# - 删
# - 改
# - 查
# In[ ]:
class Database(object):
def __init__(self):
self.hostname = '127.0.0.1'
self.port = 3306
self.password = 'pwd'
self.username = 'root'
def fetch(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def create(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def remove(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def modify(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
db = Database() # 实例一个对象
db.create() # 对象调用方法
# In[ ]:
############ 单例类定义 ###########
class Database(object):
__instance = None
def __init__(self):
self.hostname = '127.0.0.1'
self.port = 3306
self.password = 'pwd'
self.username = 'root'
def fetch(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def create(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def remove(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def modify(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
obj = Database()
print(id(obj))
obj.create()
self.write("Hello, world")
application = tornado.web.Application([
(r"/index", MainHandler),
])
if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()
# 对于上述实例,每个请求到来,都需要在内存里创建一个实例,再通过该实例执行指定的方法。
#
# 那么问题来了...如果并发量大的话,内存里就会存在非常多功能上一模一样的对象。存在这些对象肯定会消耗内存,对于这些功能相同的对象可以在内存中仅创建一个,需要时都去调用,也是极好的!!!这时单例模式出马,单例模式用来保证内存中仅存在一个实例!!!
# 通过面向对象的特性,构造出单例模式:
# In[ ]:
############# 单例类定义 ###########
class Foo(object):
__instance = None
@staticmethod
def singleton():
if Foo.__instance:
return Foo.__instance
else:
Foo.__instance = Foo() # 实例化自己
return Foo.__instance
############ 获取实例 ###########
obj = Foo.singleton()
# 对于Python单例模式,创建对象时不能再直接使用:obj = Foo(),而应该调用特殊的方法:**obj = Foo.singleton() 。**
#
# 类实例化一个对象,然后对象调用其方法,但是这样就会用很多对象了,在上面我们知道静态方法是由类调用的,不需要实例化对象。上面写了一个静态方法,实例化自己,然后再给调用者,这样来来回回调用者们只不过是反复再调用类的一个静态方法。而这个方法只实例化了一个对象。这样达到单例的效果。
# In[ ]:
############ 单例类定义 ###########
class Database(object):
__instance = None
def __init__(self):
self.hostname = '127.0.0.1'
self.port = 3306
self.password = 'pwd'
self.username = 'root'
@staticmethod
def singleton():
if Database.__instance:
return Database.__instance
else:
Database.__instance = Database()
return Database.__instance
def fetch(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def create(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def remove(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
def modify(self):
# 连接数据库
# 拼接sql语句
# 操作
pass
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
obj = Database.singleton()
print(id(obj))
obj.create()
self.write("Hello, world")
application = tornado.web.Application([
(r"/index", MainHandler),
])
if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()
# 总结:单例模式存在的目的是保证当前内存中仅存在单个实例,避免内存浪费!!