面向对象
面向对象
- 与面向过程对比
- 面向过程:数学逻辑的映射,学会做个好员工
- 面向对象:生活逻辑的映射,学会做个好领导
- 生活实例
- 类: 人 手机 电脑
- 对象: 小明、老王 你的手机、我的手机 我的电脑、你的电脑
- 官方定义
- 类:具有相同特征(属性和行为)事物的抽象
- 对象:某个类的具象
- 编程语言
- 类:是一种自定义的数据类型
- 对象:某个类类型的变量
基础语法
-
类的定义
class 类名:
pass -
使用说明:
-
定义类的关键字是class
-
类名:原则上符合标识符命名规范即可,但是通常都采用大驼峰风格命名,如:UserName
-
类名后不要忘记’:’
-
类的内容要进行整体缩进
-
属性可以通过变量体现,行为通过函数体现
-
成员访问:
- 属性:对象.属性名
- 方法:对象.方法名()
-
示例:
定义类
class Person:
# 行为通过函数体现# 吃饭 def eat(self): print('毛氏红烧肉,听说很好吃!') # 睡觉 def sleep(self): print('喜欢睡觉也是生活态度!') # 创建对象 p = Person() # print(p) # 属性可以动态添加 p.name = 'ergou' p.age = 22 print(p.name, p.age) # 调用成员方法 p.eat() p.sleep()
-
-
self使用
class Person:
def run(self):
# self表示当前对象,就是调用该方法的对象
print(’{}每天以2m/s的速度奔跑5km’.format(self.name)) -
__str__方法
class Person:
# 打印对象时默认打印:类名+地址
# 重写该方法,打印对象时会打印该方法的返回值
# 该方法必须返回一个字符串
# 使用str方法强制类型转换时也会调用该方法
def str(self):
return ‘我是{},今年{}岁’.format(self.name, self.age)p = Person() p.name = '勒布朗.詹姆斯' p.age = 34 print(p) s = str(p) print(s)
-
__init__方法
class Cat:
def str(self):
return ‘name:{},age:{},color:{}’.format(self.name, self.age, self.color)# 构造方法:创建对象后会自动调用,可以进行初始化设置 def __init__(self, name, age, color): # print('__init__') self.name = name self.age = age self.color = color # 这种方式比较繁琐 # jiafei = Cat() # jiafei.name = '加菲' # jiafei.age = 2 # jiafei.color = '橘黄色' # print(jiafei) jiafei = Cat('加菲', 2, '橘黄色') print(jiafei)
-
__del__方法
class Pig:
# 析构方法:当对象释放时,系统会自动调用
# 该方法一般做资源释放操作,如:断开数据库连接、文件关闭
def del(self):
print(‘大师兄,我不行了’)bajie = Pig() del bajie print('八戒,一路走好!')
-
思考:小明手里有两张牌,左手♥K,右手♠A,小明交换两手的牌后,手里分别是什么?
- 先找到对象:♥K、♠A、左手、右手、小明
- 根据对象抽象类:人、手、牌
- 设计相关的类,需要根据功能反复完善
常用内置函数
-
内置函数:在类中,特定的时刻会自动触发的函数
-
init、del、str
-
setattr、getattr、delattr
class Person:
def init(self, name):
self.name = name
# print(‘初始化对象’)# 当设置属性时会自动调用 def __setattr__(self, key, value): # print(key, value) self.__dict__[key] = value # 当获取不存在的属性时会自动触发 def __getattr__(self, item): if item == 'age': return 18 else: return '你猜' # 销毁对象的指定属性时会自动触发 def __delattr__(self, item): print(item, '属性即将销毁') xiaoming = Person('小明') # xiaoming.name = '小明' print(xiaoming.age) print(xiaoming.height) print(xiaoming.__dict__) del xiaoming.name print('over')
-
setitem、getitem、delitem
class Person:
# 将对象当做字典操作,添加或设置属性时自动触发
def setitem(self, key, value):
# print(key, value)
self.dict[key] = value# 将对象当做字典操作,根据键获取值时会自动触发 def __getitem__(self, item): # print(item) return self.__dict__.get(item) # 将对象当做字典操作,根据键销毁属性时会自动触发 def __delitem__(self, key): print(key, '属性即将销毁') del self.__dict__[key] xiaoming = Person() # xiaoming.name = '小明' # print(xiaoming.name) xiaoming['name'] = '小明' print(xiaoming['name']) del xiaoming['name'] print(xiaoming['name'])
-
call
class Person:
# 将对象当做函数调用,该方法会自动触发
def call(self, *args, **kwargs):
# print(‘call’, args)
return sum(args)p = Person() # 将对象当做函数调用,必须重写类的call方法 ret = p(1, 2, 3) print(ret) def test(): pass # 判断一个对象是否可以调用 print(callable(p)) print(callable(test)) # 判断一个对象是否是函数 from inspect import isfunction print(isfunction(p)) print(isfunction(test)) # 判断对象是否拥有指定属性,若对象可以调用,那么一定拥有__call__属性 print(hasattr(p, '__call__')) print(hasattr(test, '__call__'))
面向对象
面向对象三大特点
- 封装:既是对数据结构的封装,又是处理数据的方法的封装。
- 继承:强调的是父子类的关系
- 多态:不同对象调用相同的方法,有不同的响应。
类的继承
-
相关概念
- 继承:父类的属性和方法,子类可以直接拥有,称为继承。
- 派生:子类在父类的基础上衍生出新的特征(属性或行为),称为派生。
- 总结:其实他们是一回事,知识描述问题的角度不同(继承侧重相同点,派生侧重不同点)
-
继承语法
# class Animal:
# 当没有写父类时,默认继承自object
class Animal(object):
def init(self, name):
self.name = namedef run(self): print('小动物喜欢一天到晚跑个不停') # 定义一个子类,继承自Animal class Dog(Animal): pass d = Dog('旺财') # 可以直接拥有父类的属性 print(d.name) # 也拥有父类的行为 d.run()
-
派生示例
class Animal:
def run(self):
print(‘一天到晚跑个不停’)class Cat(Animal): def eat(self): print('猫喜欢吃鱼') c = Cat() c.run() # 添加的属性 c.name = '加菲' print(c.name) # 衍生的方法 c.eat()
-
方法重写
class Animal:
def run(self):
print(‘小动物喜欢到处跑’)def eat(self): print('小动物喜欢吃东西') class Cat(Animal): # 父类方法完全不合适,覆盖重写 def run(self): print('猫喜欢走猫步') # 父类的方法不够完善,需要添加完善 def eat(self): # 保留父类方法的内容 # Animal.eat(self) # 不建议使用 # super(Cat, self).eat() super().eat() # 推荐使用 # 添加完善的内容 print('猫喜欢吃鱼') c = Cat() c.eat() c.run()
-
多继承:一个类可以有多个父类
class A:
def test(self):
print(‘in class A func test…’)class B: def test(self): print('in class B func test...') def eat(self): print('in class B func eat ...') class C(A, B): def eat(self): # 默认的方式找父类,跟不重写方法时的顺序是一样的, # 按照书写的先后顺序,默认是写在前面的类的方法 # super().eat() # 明确指定调用哪个父类的方法 B.eat(self) c = C() c.test() c.eat()
-
权限管理
class Person:
def init(self, name, age):
self.name = name
# 定义私有属性
self.__age = agedef test(self): # 私有属性和方法可以在类的内部使用 print(self.__age) self.__hello() # 定义私有方法 def __hello(self): print('for test') class Man(Person): def show(self): # 私有属性和方法在子类也不可以使用 print(self.__age) p = Person('老王', 38) print(p.name) # 属性前添加连个下划线,类的外部不能使用 # print(p.__age) # 默认在添加两个下划线的属性名前添加了'_类名' # print(p.dict) # 强烈建议不要这样使用 # print(p._Person__age) p.test() # p.__hello() m = Man('小明', 18) # m.show()
-
类属性
class Person:
# 类属性,属于整个类
# nation = ‘中国’# 限制对象可以使用的属性,可以提高效率,节约存储空间 __slots__ = ('name', 'age', 'nation') def __init__(self, name): # 成员属性,属于某个对象 self.name = name # 成员属性,当不存在会会试着找一下类属性 # self.nation = 'xxx' def test(self): pass # 通过类名访问类属性 # print(Person.nation) p = Person('王大花') # 可以通过对象访问类属性,但是不建议 print(p.name) # print(p.nation) p.age = 20 # p.height = 180 # 特殊的类属性 # 类名字符串 print(Person.__name__) # 父类组成的元组 print(Person.__bases__) # 类相关的信息 print(Person.__dict__) print(Person.__slots__)
-
类方法
-
说明
- 提通过类名进行调用
- 定义时需要使用装饰器classmethod
-
作用:
- 可以创建对象或者简洁的创建对象
- 可以对外提供简单易用的接口
-
示例1:
class Person:
# 成员方法,通过对象进行调用
def eat(self):
print(‘红烧鸡腿我喜欢吃’)# 类方法,通过类名进行调用 @classmethod def test(cls): # cls表示当前类 print(cls, '类方法') # 创建对象 @classmethod def create(cls): obj = cls() obj.age = 1 return obj
p = Person()
p.eat()
Person.test()创建或者简洁的创建对象
p2 = Person.create()
print(type(p2)) -
示例2:
class Number:
def init(self, num1, num2):
self.num1 = num1
self.num2 = num2def add(self): return self.num1 + self.num2 def sub(self): return self.num1 - self.num2 def mul(self): return self.num1 * self.num2 def div(self): if self.num2 == 0: return None return self.num1 / self.num2 # 对外提供简单易用的接口 @classmethod def pingfanghe(cls, num1, num2): n1 = cls(num1, num1) n12 = n1.mul() n2 = cls(num2, num2) n22 = n2.mul() n3 = cls(n12, n22) return n3.add() # 计算3^2 + 4^2 # n1 = Number(3, 3) # n12 = n1.mul() # n2 = Number(4, 4) # n22 = n2.mul() # n3 = Number(n12, n22) # ret = n3.add() # print(ret) print(Number.pingfanghe(3, 4))
-
-
静态方法
-
说明:
- 通过装饰器staticmethod修饰
- 通过类名进行调用
-
示例:
class Person:
# 静态方法:没有cls参数
@staticmethod
def test():
print(‘static method test …’)# 静态方法:可以创建对象 @staticmethod def create(): p = Person() p.age = 1 return p Person.test() p = Person.create() print(type(p))
-
总结:
- 凡是静态方法能够解决的问题类方法都能解决
- 若方法中没有涉及类名的操作,可以使用静态方法代替类方法
-
多态特性
-
定义:不同的对象,调用相同的方法,会有不同的响应
-
示例:
class Animal:
def run(self):
print(‘小动物走道都不一样’)class Cat(Animal): def run(self): print('猫都的是猫步') class Dog(Animal): def run(self): print('狗一般都走S型') def func(obj): obj.run() func(Cat()) func(Dog())
属性函数
-
说明:可以将成员方法当做属性一样访问
-
作用:获取时以及设置指定属性时都可以进行人为干预,可以保护特定属性
-
示例:
class User:
def init(self, username, password):
self.username = username
self.__password = password# 可以将方法像访问属性一样访问 @property def test(self): return 'hello' # 保护指定的属性 @property def password(self): print('大哥,有人想偷看密码') return '哈哈,让你偷看,没门' # 在设置对应属性时会自动调用 @password.setter def password(self, password): print('密码设置', password) # 人为干预密码的设置过程,如:加密存储密码 self.__password = 'xxx' + password + 'yyy' u = User('大狗', '123456') # u.test() # print(u.test) print(u.password) u.password = '654321' print(u.__dict__)
虚拟环境
- 为什么要使用虚拟环境?
- 在同一个环境下,不同软件依赖同一软件包的不同版本,为了进行环境隔离就出现了虚拟环境
- 虚拟环境创建工具:virtualenv
- pip install virtualenv
- 创建虚拟环境
- virtualenv 虚拟环境目录名
- 激活虚拟环境
- 虚拟环境目录\Scripts\activate.bat
- 退出虚拟环境
- 虚拟环境目录\Scripts\deactivate.bat
- 快速复制一个虚拟环境
- 冷藏现有的虚拟环境:pip freeze > requirements.txt
- 创建一个新的虚拟环境:virtualenv venv
- 激活虚拟环境:venv\Scripts\activate.bat
- 安装依赖:pip install -r requirements.txt
- 虚拟环境管理工具:virtualenvwrapper-win
- 安装:pip install virtualenvwrapper-win
- 说明:默认会将虚拟环境保存在C:\Users\用户名\Envs目录下
- 查看虚拟环境:lsvirtualenv
- 创建虚拟环境:mkvirtualenv
- 启动虚拟环境:workon 虚拟环境名
- 退出虚拟环境:deactivate.bat
import pickle
class Person:
def init(self, name, age):
self.name = name
self.age = age
def __str__(self):
return 'name:{} age:{}'.format(self.name, self.age)
xiaoming = Person('xiaoming', 20)# print(xiaoming)
# 序列化:会将对象转换为bytes
# s = pickle.dumps(xiaoming)
# print(s)
# 反序列化:从bytes中解析出对象
# xm = pickle.loads(s)
# print(xm, type(xm))
# 直接保存到文件
# fp = open('data.txt', 'wb')
# pickle.dump(xiaoming, fp)
# fp.close()
# 从文件中读取对象
fp = open('data.txt', 'rb')
xm = pickle.load(fp)
print(xm, type(xm))