前言
python学习笔记 (仅供学习使用)
在Python中,面向对象还有很多高级特性,允许我们写出非常强大的功能。
python是动态语⾔,动态编程语⾔ 是 ⾼级程序设计语⾔ 的⼀个类别,在计算机科学领域已被⼴泛应⽤。它是⼀类在 运⾏时可以改变其结构 的语⾔ :例如新的函数、对象、甚⾄代码可以被引进,已有的函数可以被删除或是其他结构上的变化。动态语⾔⽬前⾮常具有活⼒
一、动态给类和对象赋予属性和函数
import types
class Person(object):
def __init__(self,x_name,x_age):
self.name = x_name
self.age = x_age
if __name__ == '__main__':
p = Person('zhangsan',23)
# 1、 可不可以动态的给对象p赋予一个新的对象属性
Person.address="北京"
p.sex ='男'
print(p.sex)
# 2、 可以给当前对象p 动态的赋予一个新的对象函数吗
def run(self,work):
print('%s正在完成的工作是:%s'%(self.name,work))
p.run =types.MethodType(run,p) # 给对象p添加一个实例(成员,对象)函数
p.run('学习')
# 3、给Person类动态赋予一个类函数
@classmethod
def class_run(cls,work):
print("类函数中的work是:%s"%work)
Person.work = class_run
p.work('学习python')
# 4、 给Person类动态赋予一个静态函数
@staticmethod
def static_run(work):
print('静态函数中的work是:%s'%work)
Person.staticRun = static_run
p.staticRun('学习全栈')
Person.staticRun('学习全栈')
那既然有增加,就有删除
删除对象与属性的方法
- del 对象.属性名
- delattr(对象, “属性名”)
我们知道,正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。先定义class:然后尝试给实例绑定一个属性,还可以绑定一个方法,但是一个实例方法对另一个实例不起作用,那就得给类整个类绑定一个方法或属性,这样所有的实例都可以调用
需要注意的是我们的动态语言在运行后还能修改的,但是静态语言是不可以的,这就会造成不严谨
二、slots(限制属性的增加)
正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
动态语⾔:可以在运⾏的过程中,修改代码(python)
静态语⾔:编译时已经确定好代码,运⾏过程中不能修改
但是,如果我们想要限制class的属性怎么办?比如,上例只允许对人类的实例添加 name 和 age 属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的 slots 变量,来限制该class能添加的属性:
#限制类动态增加属性
class Student(object):
#只允许当前Student拥有name和sex属性
__slots__ = ('name','sex')
#验证slots
s = Student()
s.name='lisi'
s.sex= '女'
# Student.age =10 类属性没有限制 ,可以动态添加
s.age =23
print(s.age)
run:
Traceback (most recent call last):
File "/Person.py", line 54, in <module>
s.age =23
AttributeError: 'Student' object has no attribute 'age'
三、@property装饰器
在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改
@property类似于java中的getset方法。
class Person(object):
# age 属性的值限制的范围是0——88
def get_age(self):
return self._age
def set_age(self,value):
if value>=0 and value <=88:
self._age=value
else:
# print("age的值必须在0到88之间")
self._age=0 # 0为age的初始值
raise ValueError('age的值必须在0到88之间')#raise是抛出异常。
class Person_Property(object):
# age 属性的值限制的范围是0——88
@property #age 属性暴露出去
def age(self):
return self._age
@age.setter # 当前age属性可以允许赋值
def age(self,value):
if value>=0 and value <=88:
self._age=value
else:
# print("age的值必须在0到88之间")
self._age=0 # 0为age的初始值
raise ValueError('age的值必须在0到88之间')
@property
def name(self):
self._name = "张三"#此处必须使用_name,表私有属性
return self._name
if __name__ == '__main__':
p = Person_Property()
p.age = 18
print(p.age) # age属性可以读,可以写
# p.name = "Lisi"
print(p.name) # name属性是只读的
四、多重继承
继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能:
class Father(object):
def work(self):
print("父亲在工作")
class Mather(object):
def work(self):
print("母亲在工作")
class Child(Mather,Father): # 多继承
def __init__(self,xname):
self.name = xname
def work(self):
print("自己在工作")
if __name__ == '__main__':
c = Child('张三')
c.work() # 如果多个父类中都有同样的函数,按照优先级来调用
print(Child.__mro__) # 打印Child的继承结构,并且按照优先级
run:
自己在工作
(<class '__main__.Child'>, <class '__main__.Mather'>, <class '__main__.Father'>, <class 'object'>)
五、定制类
看到类似 slots 这种形如 xxx 的变量或者函数名就要注意,这些在Python中是有特殊用途的。
slots 我们已经知道怎么用了, len() 方法我们也知道是为了能让class作用于 len() 函数。这些在Python有另外的一些名称叫魔术方法
除此之外,Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类。
class Person(object):
def __init__(self,xname):#重写初始化函数
self.name = xname
# 斐波拉契数列前两个值是固定
self.a ,self.b = 0,1
# 用于定制对象的描述信息
def __str__(self):
return "Person object (name :%s)"%self.name
# person默认不是可迭代对象,变成一个可迭代对象,必须返回一个迭代器
def __iter__(self): # 生成一个斐波拉契数列
return self
# person就变成一个迭代器
def __next__(self):
self.a ,self.b = self.b, self.a+self.b # 计算下一个值
if self.a > 1000: # 如果出现一个大于1000的数字,退出循环
raise StopIteration
return self.a
# def __getitem__(self, item):
# a, b = 1, 1
# for x in range(item):
# a, b = b, a + b
# return a
# person就可以类似于list
def __getitem__(self, item): # item 可能是一个下标,还有可能是一个切片(范围)
if isinstance(item,int): # item是一个下标,如果是整数
a,b =1,1
for x in range(item):
a ,b = b,a+b
return a
elif isinstance(item,slice): # item是一个范围,如果是范围
start = item.start
stop = item.stop
if start is None:
start = 0 # start默认值
a,b =1,1
L = []
for x in range(stop):
if x>=start:
L.append(a)
a,b =b,a+b
return L
# 当访问person对象中不存在的属性和函数的时候会抛出AttributeError,如果不想看到这个error则需要重写
def __getattr__(self, item):
if item =='age':
return 18
elif item == 'eat':
return lambda : print('eat函数执行')
def __call__(self, *args, **kwargs):
print('person 对象可以调用')
if __name__ == '__main__':
p = Person('张三')
print(p)
for n in p:
print(n)
# p 有的类似于一个list,在list还可以切片
print("下标为5的值是:",p[5])
print("下标为5-10的值是:",p[5:10])
print(p.age)
p.eat()
p() #把person对象当成一个函数,直接调用,person对象 ==一个函数
print(callable(p)) # 判断出来p对象,是不是可以调用的对象 (函数)
run:
Person object (name :张三)
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
下标为5的值是: 8
下标为5-10的值是: [8, 13, 21, 34, 55]
18
eat函数执行
person 对象可以调用
True
六、枚举类
但是这样的定义的类型是 int ,并且仍然是变量,并且在运算中,无法时时创建对应的值,当然这种指代是以更好的方式去使用变量数值。
这里存在更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了 Enum 类来实现这个功能。
from enum import Enum
# 枚举中:一个名字 对应一个值 第一种:定义一个枚举
Month = Enum('Month',('jan','feb','mar','apr','may','jun','jul'))
# 把整个枚举中的所有值遍历出来
print(Month.__members__) # 枚举中的值自动从1开始,不会重复
#得到二月份的值
print(Month['feb'].value)
# 根据值(2)来得到月份的名字
print(Month(2).name)
# 定义一个颜色的常量枚举
class Color(Enum): # 第二种:自定义一个枚举类
red = 100
green =200
blue =30
yellow =200 # 不允许key相同或者value。如果value重复了根据value取name只能得到第一个
print(Color(200))
run:
{'jan': <Month.jan: 1>, 'feb': <Month.feb: 2>, 'mar': <Month.mar: 3>, 'apr': <Month.apr: 4>, 'may': <Month.may: 5>, 'jun': <Month.jun: 6>, 'jul': <Month.jul: 7>}
2
feb
Color.green