在此感谢前辈们的指导:http://python.jobbole.com/80955/
https://www.cnblogs.com/wupeiqi/p/4766801.html
https://www.cnblogs.com/paomaliuju/p/5122761.html
https://www.cnblogs.com/goser/articles/7097728.html
http://www.cnblogs.com/alex3714/articles/5213184.html
https://www.cnblogs.com/cenyu/p/5713686.html
本文是各篇文章优点的综合,适合有一定基础的朋友进行个人知识梳理
为了表示对前辈们的尊敬,以下均为纯手打,代码已经在2.7和3.6.3版本的python中测试过,并进行了修改,和注释整理,以确保最通俗易懂。
首先回顾一下上节知识点
1,面向对象是一种很重要的编程方式,此编程方式的实现主要是通过类和对象的使用来实现的
2,在类中储存有多个需要的函数
3,对象,使用类这个模板,来进行更加具体的实例化,主要用于调用被包装在类中的函数。
4,面向对象三大属性:封装,继承和多态
5,python中新式类和经典类的粗浅比较用法
6,深度优先和广度优先的第一核心概念解释以及第二爬虫中具体运用的解释。
那么这次分享,将会是如下部分:
面向对象高级语法部分
-
经典类vs新式类核心
-
字段,属性
-
静态方法、类方法、属性方法
-
类的特殊方法
-
反射
谢谢大家啦(微笑)。
第一部分:
五星级:经典类和新式类的比较用法(重要)
(注:经典类的代码有的需要在2.7中运行,请提前下好)
在python2及以前的版本中,由任意内置类型派生出来的类(只要有一个内置类型位于类树的某个位置)都属于新式类,反之,即不由任意内置类型派生出来的类,则称之为“经典类”。
关于内置类型的名词解释:
新式类”和“经典类”的区分在python3
“内置类型是指任何语言在设计初期定义的类型,如c语言中的int, double, char... 它也是在一种语言中最基本的类型,
与编译器编译出的代码具有重大关系.
"新式类"和“经典类”的区分在python3中就已经不存在了,而在之后的版本里,所有的类都派生出了自类置类型object,即所有的类都是新式类
经典类继承算法是深度优先,新式类则是广度优先
class A():#此代码在2.7.14中运行,也可以在3.6.3中运行,但是许多大型的经典类实例,只能在2.7中运行
var='classic class A'
class B(A):
pass
class C():
var='class C'
class sunclass(B,C):
pass
if __name__=='__main__':
print(sunclass.var)
最后结果:classic class A
再来看新式类
class A(object):
var='classic class A'
class B(A):
pass
class C(A):
var='class C'
class sunclass(B,C):
pass
if __name__=='__main__':
print(sunclass.var)
最后结果:class C
这恰好就是上节分享的深度优先和广度优先
但是值得注意的是,新式类并非全部都是广度优先,而是采用c3算法(这一部分以后继续进行介绍)
可是为什么python3之中,会出现新式类呢,经典类用的不是挺好的吗,这个需要通过具体问题来进行分析
在经典类中所有的类都是classobj类型,而类的实例是instance类型,类与实例只有通过__class__属性进行关联,这样在判断实例时,就会造成不便,所有的类都是instance类型
class A():pass#此代码在python32.7中运行
class B():pass
a = A()
b = B()
if __name__ == '__main__':
print(type(a))
print(type(b))
print(type(a) == type(b))
最后结果
<type 'instance'>
<type 'instance'>
True
type(a) == type(b)的结果永远为True,那这样的比较就毫无意义。
更为麻烦的是,经典类的实例是instance类型,而内置类的实例却不是,无法统一。
但是在python3中却没有这样的要求
同样代码,在python3中的运行结果
<class '__main__.A'>
<class '__main__.B'>
False
综上:
Python3中所有的类都是新式类,新式类中类与类型已经统一:类实例的类型是这个实例所创建自的类(通常是和类实例的__class__相同),而不再是Python 2.x版本中的“instance”实例类型。
五星级:字段,属性(重要)
字段分为,静态字段和普通字段
静态字段:属于类
普通字段:属于对象,
让我们来通过一个小代码,了解什么是静态字段,和普通字段
class Provinces:
#静态字段
country='中国'
def __init__(self,name):
self.name=name#普通字段
#直接访问普通字段
obj=Provinces('河北省')
print(obj.name)
#直接访问静态字段
print(Provinces.country)
静态字段在内存中只保存一份
普通字段在每个对象中都要保存一份
属性:
属性有两个知识点:
1,属性的基本使用
2,属性的两种定义方式
一:属性的基本使用:
class Foo:
def func(self):
print('求魔')
#定义属性
@property#定义属性之前的必须操作
def pro(self):
return "wu pei qi"
foo_obj=Foo()
foo_obj.func()
c=foo_obj.pro
print(c)
属性的定义和调用注意以下几点:
1:定义时,在普通方法的基础上调用@property方法
2:定义时,属性中仅有一个self函数
3:调用时无需括号
foo_obj.func()
c=foo_obj.pro
属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象
属性由方法变种而来,如果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)
print(p.start)#就是起始值,即:m
print(p.end) #就是结束值,即:n
属性的两种定义方式
1,装饰器 即:在方法上运用装饰器
2,静态字段 ,即在类中定义值为property对象的静态字段
第一种装饰器方式:(在类的普通方法上应用@property装饰器)
(此处只介绍新式类的方法)
#新式类:
class Goode(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=Goode()
obj.price
obj.price=123
del obj.price # 自动执行 @price.deleter 修饰的 price 方法
新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Goode(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
print('%s'%(value))
@price.deleter
def price(self,value):
del self.original_price
obj=Goode()
print(obj.price)
obj.price=81
静态字段方式,创建值为property对象的静态字段
class Foo:
def get_bar(self):
return 'wupeiqi'
BAR=property(get_bar)
obj=Foo()
result=obj.BAR
print(result)
这是一个小实例
我们再来看一个蛮经典的实例
class Foo(object):
def __init__(self,name):
self.name=name
def get_bar(self):
return self.name
def set_bar(self,value):
self.name=value
def del_bar(self):
del self.name
BAR=property(get_bar,set_bar,del_bar,'desadfas')
obj = Foo('100')
print(obj.BAR) # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex"#print(obj.)# 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
print(obj.BAR)
del Foo.BAR # 自动调用第三个参数中定义的方法:del_bar方法,进行删除
class Goods(object):
def __init__(self):
self.origina_price=100
self.discount=0.8
def get_price(self):
new_price=self.origina_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 # 获取商品价格
obj.PRICE = 200 # 修改商品原价
del obj.PRICE # 删除商品原价
第三部分:静态方法、类方法、属性方法
1,静态方法
简单理解:通过@staticmethod装饰器即可把其装饰的一个方法转变为静态方法,但什么是静态方法,普通方法必须实例化之后才能调用,但是静态方法,已经跟类本身没有关系,它与类的为唯一关联是需要通过类名才能调用这个方法
class Dog(object):
def __init__(self,name):
self.name=name
@staticmethod
def eat(self):
print("%s is eating"%self.name)
d=Dog("dsfas")
d.eat()
上述会出现一系列错误
因为当eat变成静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了。
而要解决它的方式有两种
一个是把d当成self传入当中
另一个就是把self去掉
class Dog(object):
def __init__(self,name):
self.name = name
@staticmethod
def eat():
print(" is eating")
d = Dog("ChenRonghua")
d.eat()
即可
2,类方法
类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量
class Dog(object):def __init__(self,name):
self.name=name
@classmethod
def eat(self):
print("%s is eating" %self.name)
d=Dog("chenronghua")
d.eat()
这段代码,执行时会进行报错
所以,我们需要定义一个类变量
class Dog(object):name = "我是类变量"
def __init__(self, name):
self.name = name
@classmethod
def eat(self):
print("%s is eating" % self.name)
d = Dog("ChenRonghua")
d.eat()
3,属性方法
刚才我们提到了属性变量,其实属性变量就是属性方法
这次我们来做一个小案例
比如 ,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了, 想知道这种状态你必须经历以下几步:
1. 连接航空公司API查询
2. 对查询结果进行解析
3. 返回结果给你的用户
lass Flight(object):def __init__(self,name):
self.flight_name=name
def checking_status(self):
#print('检查飞机的运行状态:'%self.flight_name)
return 1
@property
def flight_status(self):
status=self.checking_status()
if status==0:
print('飞机取消了')
elif status==1:
print('飞机飞行之中')
elif status==2:
print('飞机降落了')
else:
print("不能够证明飞机的运行状态")
@flight_status.setter
def flight_status(self,status):
status_dic={
0:"canceled",
1:"arrived",
2:"depatured"
}
print("\033[31;1m已经修改了飞行的状态到 \033[0m", status_dic.get(status))
@flight_status.deleter
def flight_status(self):
print("status got removed")
f=Flight("波音777")
f.flight_status
f.flight_status=2
好了,非常基础的东西就讲完了,我们来讲一点比较偏门的,毕竟学习一门语言还要了解他的一些源代码,知道是怎样运行的,才能不断精进
类的特殊成员用法:
1,__doc__:表示类的描述信息:
在开发中,我们需要在代码中多加注释,来增强代码的可读性,用__doc__就可以查看函数或类的描述信息
def test():'''
this is a test function
'''
pass
class ClassTest(object):
'''
this is a class method
'''
print(test.__doc__)
print(ClassTest.__doc__)
这个函数可以用来查看注释
2、__module__和__class__
__module__ 表示对象所在的模块
__class__ 表示对象所在的类是什么
3,__init__ 构造方法,通过类创建对象时,自动触发执行
5. __call__ 对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('call')
obj=Foo()
obj()
6.__dict__ 查看类或对象中的所有成员
country='China'
def __init__(self,name,count):
self.name=name
self.count=count
def func(self,*args,**kwargs):
print('func')
print(Province.__dict__)#获取类的成员,静态字段,方法
obj1=Province('HeBei',10000)
print(obj1.__dict__)#获取obj1的成员
7.__str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值
class
Foo:
def
__str__(
self
):
return
'alex li'
obj
=
Foo()
print
(
obj)
# 输出:alex li
8.__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据
class Foo(object):def __getitem__(self, item):
print('__getitem',item)
def __setitem__(self, key, value):
print('__setitem__',key,value)
def __delitem__(self, key):
print('__delitem__',key)
obj=Foo()
result=obj['k1']
obj['k2']='alex'
del obj
最后一部分:
反射即想到4个内置函数分别为:getattr、hasattr、setattr、delattr 获取成员、检查成员、设置成员、删除成员现对这几个函数进行细致的分享,
getattr(object, name[,default])
获取对象object的属性或者方法,如果存在即打印出来,如果不存在,即打印出默认值,默认值可选
需要注意的是,如果返回的是对象的方法,返回的是方法的内存地址,如果运行这个方法,可以在后面添加一对括号
class test():name="xiaohua"
def run(self):
print( "helloworld")
t=test()
print(getattr(t,"name"))#获取name属性,存在就打印出来。
print(getattr(t, "run")) #获取run方法,存在就打印出方法的内存地址。
getattr(t, "run")() #获取run方法,后面加括号可以将这个方法运行。
print(getattr(t, "age","18")) #若属性不存在,返回一个默认值。
hasattr(object, name)判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False。需要注意的是name要用括号括起来
class test():name="xiaohua"
def run(self):
print( "helloworld")
t=test()
print(hasattr(t,"name"))#判断对象有name属性
print(hasattr(t, "run")) #判断对象有run方法
setattr(object, name, values)
给对象的属性赋值,若属性不存在,先创建再赋值
class test():name="xiaohua"
def run(self):
print( "helloworld")
t=test()
print(hasattr(t,"age"))#判断属性是否存在
setattr(t, "age", "18") #为属相赋值,并没有返回值
print(hasattr(t, "age") ) #属性存在了
综合运用
class Foo(object):
def __init__(self):
self.name = 'wupeiqi'
def func(self):
return 'func'
obj = Foo()
#### 检查是否含有成员 ####
print(hasattr(obj, 'name'))
print(hasattr(obj, 'func'))
#### 获取成员 ####
getattr(obj, 'name')
getattr(obj, 'func')
#### 设置成员 ####
setattr(obj, 'age', 18)
setattr(obj, 'show', lambda num: num + 1)
#### 删除成员 ####
delattr(obj, 'name')
#delattr(obj, 'func')#不能用于删除方法,否则报错