如何定义一个类?
class 类名:
pass
怎样通过类,创建出一个对象?
根据类创建对象
one = Money()
执行流程
1. 类的定义
2. 根据类,创建出一个对象
3. 将对象的唯一标识返回
python
复制代码
class Money:
pass
print(Money.__name__)
xxx = Money
print(xxx.__name__)
Money = 666
print(id(Money))
print(Money)
# 根据这个类,创建(实例化)一个对象
one = Noney()
print(one)
print(one.__class__)
属性相关
属性和变量的区别及判定依据?
区别
概念
变量是"可以改变的量值"
属性是"属于某个对象的特性"
访问权限
变量:根据不同的位置,存在不同的访问权限
全局变量
局部变量
...
属性:只能通过对象来进行访问
所以,必须先找到对象
对象也是通过变量名来引用;而既然是变量,也有对应的访问权限
判定依据
是否存在宿主
对象属性
怎样让一个对象拥有一些属性?(增)
1. 直接通过对象,动态添加
语法:
对象.属性 = 值
2. 通过类的初始化方法(构造方法)
__init__方法
python
复制代码
# _*_ encoding:utf-8 _*_
# 1. 定义一个类
class Person:
pass
# 2. 根据类, 创建一个对象
p = Person()
# 3. 给p对象, 增加一些属性
p.age = 18
p.height = 180
# 4. 验证是否有添加成功
print(p.age)
print(p.__dict__)
怎样访问一个对象的属性?(查)
一般访问
对象.属性
如果访问不到
会直接报错
需要记住错误提示
怎样修改一个对象的属性?(改)
同新增一样;系统会自动识别,不存在则新增,存在则修改
语法
对象.属性 = 值
python
复制代码
# _*_ encoding:utf-8 _*_
# 1. 定义一个类
class Person:
pass
# 2. 根据类, 创建一个对象
p = Person()
# 3. 给p对象, 增加一些属性
p.age = 18
p.height = 180
p.pets = ["小花", "小黑"]
print(p.pets, id(p.pets))
p.pets.append("小黄")
print(p.pets, id(p.pets))
怎样删除一个对象的属性?(删)
del 对象.属性
python
复制代码
# num = 10
# del num
# print(num)
class Person:
pass
p.age = 18
del p.age
print(p.age)
补充:
查看对象的所有属性?
对象.__dict__
不同对象之间不能互相访问对方的属性
python
复制代码
_*_ encoding:utf-8 _*_
# 1. 定义一个类
class Person:
pass
p1 = Person()
p2 = Person()
p1.age = 18
p2.address = "上海"
print(p1.__dict__)
print(p1.address) # 报错
类属性
万物皆对象,类也是一个对象
比如,生产月饼的模板,对于月饼来说它是"类"
但模板本身是不是也是一个具体的东西,也是由其他的东西创建出来的??
怎样让一个类拥有属性?(增)
方式1
类名.类属性 = 值
方式2
class Dog:
dogCount = 0
python
复制代码
class Money:
pass
# one = Money()
# one.age = 10
# one.age = 18
Money.count = 1
Money.age = 18
Money.num = 666
print(Money.count)
print(Money.__dict__)
class Money:
age = 18
count = 1
num = 666
怎样查询一个类的属性?(查)
通过类访问
类名.类属性
通过对象访问
对象.类属性
注意:
为什么可以通过对象访问到类属性?
答:和Python对象的属性查找机制有关
优先到对象自身去查找属性
找到则结束
如果没有找到
则根据__class__找到对象对应的类
到这个类里面查找
python
复制代码
class Money:
age = 18
count = 1
num = 666
one = Money()
print(one.__class__)
print(one.age)
print(one.count)
print(one.num)
python
复制代码
class Test:
sex = "男"
class Money:
age = 18
count = 1
num = 666
one = Money()
one.sex = "女"
one.__class__ = Test
print(one.__class__)
print(one.sex) # 打印结果为女
print(one.age) #会报错
怎样修改一个类的属性?(改)
通过类名改
语法如同给类增加一个属性的方式1
系统也会自动检测
不存在,则新增
存在,则修改
类名.属性 = 值
能否通过对象改?
不能!
python
复制代码
class Money:
age = 18
count = 1
num = 666
Money.age = 22
one = Money()
one.xxx = 999
print(one.xxx)
one.age = 13
print(one.age) #结果为14,赋值语句只能修改对象本身
print(Money.age)
print(one.__dict__)
怎样删除一个类的属性?(删)
通过类名删除
del 类名.属性
能否通过对象删除?
不能!
del 语句只删除直系属性
python
复制代码
class Money:
age = 18
count = 1
num = 666
one = Money()
del one.age # 会报错
注意
类属性的内存存储问题
一般情况下,属性存储在__dict__的字典当中
有些内置对象没有这个__dict__属性
一般对象可以直接修改__dict__属性
但类对象的__dict__为只读;默认无法修改
可以通过setattr方法修改
类属性被各个对象共享
类属性修改之后,所有的对象访问到的类属性都会跟着修改
python
复制代码
class Money:
age = 18
name = "sz"
# Money.__dict__ = {"sex": "男"} #会报错,类对象的属性不可以更改
# Money.__dict__["age"] = 20 # 会报错,类对象的属性不可以更改
补充
查看一个类的所有属性
类名.__dict__
对象属性和类属性之间的区别联系?
存储不同
抽象层级不同
宿主不同
案例考察
类
class Person:
count = 1
对象
p = Person()
问
p.count += 1
代码执行之后
Person.count 与 p.count 打印的结果分别是多少?
如何限制对象的属性?
通过设置类属性:__slots__
这个属性是一个列表
列表中的元素,即为通过这个类创建出的对象可以添加的对象属性
如果这个类实例出的对象,添加了非列表之内的属性,则会报错
python
复制代码
class person:
__slots__ = ["age"]
pass
p1 = person
p1.age = 1
p1.num = 2 # 报错
方法相关
方法的概念
描述一个目标的行为动作
比如描述一个人怎样吃,怎样喝,怎样玩...
和函数非常类似
都封装了一系列行为动作
都可以被调用的之后,执行一系列行为动作
最主要的区别就是:调用方式
方法的划分
实例方法
默认第一个参数需要接收到一个实例
类方法
默认第一个参数需要接收到一个类
静态方法
静静的看着前面俩装逼,第一个参数啥也不默认接收
注意
1. 划分的依据是:方法的第一个参数必须要接收的数据类型
2. 不管是哪一种类型的方法,都是存储在类当中;没有在实例当中的
3. 不同类型方法的调用方式不同
但,不管怎么调,把握一个原则
不管是自己传递,还是解释器帮我们处理;最终要保证不同类型的方法第一个参数接收到的数据,是他们想要的类型
python
复制代码
class Person:
def eat2():
print("这是一个实例方法")
@classmethod
def leifangfa(cls):
print("这是一个类方法", cls)
@staticmethod
def jingtaifangfa():
print("这是一个静态方法")
p = Person()
print(p)
p.eat2()
# Person.eat2() # 会报错
Person.leifangfa()
Person.jingtaifangfa()
print(person.__dict__)
print(p.__dict__)
def run():
print("run")
p.age = run
print(p.__dict__)
实例方法
class Person:
def run(self):
pass
类调用
注意
必须传递一个对象,因为实例方法要求呀。该方法没有实际意义,相当于将实例方法当作函数在调用
对象调用【主流】
不用手动传,解释器会默认把调用对象本身传递过去
注意
一般使用对象来调用
python
复制代码
class Person:
def eat(self, food):
print("在吃饭,",self, food)
print("在吃饭,", food)
def eat2():
print("123")
def eat3(xxx):
print(xxx)
p = Person()
print(p)
p.eat("土豆") # 对象调用(标准调用)
print(Person.eat)
Person.eat(123, "abc") # 类调用
func = Person.eat # 间接调用
func("abc", 999)
# p.eat2() # 会报错
p.eat3() # 第一个参数,形参名字并不重要,解释器都会将示例对象传递到第一个参数
类方法
class Person:
@classmethod
def countPerson(cls):
pass
类调用
不用手动传递第一个参数,会自动的把调用的类本身给传递过去
对象调用
不用手动传递第一个参数,会自动的把调用的对象对应的类给传递过去
注意
一般使用类来调用
python
复制代码
class Person:
# def run(self):
# print(self)
@classmethod
def leifangfa(cls, a):
print("这是一个类方法", cls, a)
Person.leifangfa(123)
p = Person()
p.leifangfa(666)
func = Person.leifangfa
func(111)
# 装饰器的作用: 在保证原本函数不改变的前提下, 直接给这个函数增加一些功能
class A(Person):
pass
A.leifangfa(0) # 打印的是类A
静态方法
class Person:
@staticemethod
def countPerson():
pass
类调用
直接调用就可以, 不需要考虑第一个参数
对象调用
直接调用就可以
注意
具体使用谁来调用,根据场景,哪个比较适合方便就使用哪个
python
复制代码
class Person:
@staticmethod
def jingtai():
print("这是一个静态方法")
Person.jingtai()
p = Person()
p.jingtai()
func = Person.jingtai
func()
不同类型的方法访问不同类型的属性的规律
python
复制代码
class Person:
age = 0
def shilifangfa(self):
print(self)
print(self.age)
print(self.num)
@classmethod
def leifangfa(cls):
print(cls)
print(cls.age)
print(cls.num)
@staticmethod
def jingtaifangfa():
print(Person.age)
p = Person()
p.num = 10
p.shilifangfa()
# p.leifangfa() # 会报错,因为类方法只能访问类属性,访问不到实例的属性
Person.leifangfa()
Person.jingtaifangfa()
# # 类属性
print(Person.age)
print(p.age)
# # 实例属性
print(p.num)
补充
函数和方法的区别?
是否有宿主
函数都是独立的个体,函数之间几乎没有共享的数据
方法有宿主
self-注意点
代表调用的对象
补充
类相关补充
元类
概念
创建类对象的类
对象怎样产生的?
由类创建出来的
类是不是对象?
是
所以,类对象是不是由另外一个类创建出来的?
是
元类
结构图
python
复制代码
num = 10
print(num.__class__)
s = "abc"
print(s.__class__)
class Person:
pass
p = Person()
print(p.__class__)
print("-"*20)
# 元类
print(int.__class__) # 输出结果:<class 'type'>
print(num.__class__.__class__) # 输出结果:<class 'type'>
print(str.__class__) # 输出结果:<class 'type'>
print(Person.__class__) # 输出结果:<class 'type'>
print(Person.__class__.__class__) # 输出结果:<class 'type'>
print(type.__class__) # 输出结果:<class 'type'>
类对象的创建方式以及创建流程
创建方式
通过class 定义
当我们这么写这个类描述的时候, 解释器会自动的帮我们创建对应的类对象
通过type函数进行创建
类的创建流程
1. 检测类中是否有明确 __metaclass__属性
有, 则通过指定元类来创建这个类对象
2. 检测父类中是否存在__metaclass__属性
有, 则通过指定元类来创建这个类对象
3. 检测模块中是否存在__metaclass__属性
有, 则通过指定元类来创建这个类对象
4. 通过内置的type这个元类,来创建这个类对象
python
复制代码
# -----------------------------类的创建方式---------------------------
class Person:
count = 0
def run(self):
pass
num = 10
print(type(num)) # 输出结果:<class 'int'>
def run(self):
print("---", self)
xxx = type("Dog",(),{"count": 0, "run": run})
print(xxx)
print(xxx.__dict__)
d = xxx()
print(d)
d.run()
python
复制代码
# -----------------------------类的创建流程------------------
class Test():
pass
#最后通过type元类创建这个对象
#type
# 再其次模块中查找元类
__metaclass__ = Test
# 其次父类级查找元类
class Animal:
# __metaclass__ = Test
pass
# 首先类中查找元类
class Person(Animal): # 继承自Animal
# __metaclass__ = Test #由metaclass指定元类
pass
class Dog(Animal):
__metaclass__ = xxx
pass
print(Person.__metaclass__)
元类的应用场景
1) 拦截类的创建
2) 修改类
3) 返回修改之后的类
后面补充
类的描述
目的
方便理清逻辑思路
方便多人合作开发时的沟通
方便生成项目文档
...
描述方式
直接在类的下方,使用三个 "双引号"对就可以
需要注明类的作用, 以及类属性描述
至于方法, 则直接在方法下,使用三个"双引号"对描述即可
作用
参数
返回值
python
复制代码
class Person:
"""
关于这个类的描述, 类的作用, 类的构造函数等等; 类属性的描述
Attributes:
count: int 代表是人的个数
"""
# 这个表示, 是人的个数
count = 1
def run(self, distance, step):
"""
这个方法的作用效果
:param distance: 参数的含义, 参数的类型int, 是否有默认值
:param step:
:return: 返回的结果的含义(时间), 返回数据的类型int
"""
print("人在跑")
return distance / step
def __init__(self):
self.__name = "sz"
help(Person)
def xxx():
"""
这是一个xxx函数, 有xxx作用
:return:
"""
print("xxx")
生成项目文档(补充)
方式1
使用内置模块
pydoc
具体步骤
前置步骤:打开控制台,进入当前项目所在目录
查看文档描述
python3 -m pydoc 模块名称
启动本地服务, 浏览文档
python3 -m pydoc -p 端口号
eg:python3 -m pydoc -p 1234
生成指定模块html文档
python3 -m pydoc -w 模块名称
bash
复制代码
python3 --help //查看python3命令
python3 -m //以一个脚本的形式运行某个库文档
python3 -m pydoc -h //查看相关使用方式
方式2
使用三方模块
Sphinx
epydoc
doxygen
具体步骤
目前感兴趣了可以先自行研究
后续讲完"包和模块"之后,会进行补充
属性相关补充
私有化属性
概念
是指将一些原本公开的属性设置权限, 只能小范围访问, 其他地方访问不了
意义
保证数据的安全性
提高代码的可维护性
注意
Python并没有真正的私有化支持,但是, 可以使用下划线完成伪私有的效果
类属性(方法)和实例属性(方法)遵循相同的规则
x
公有属性
类内部访问 【√】
子类内部访问 【√】
模块内其他位置访问 【√】
类访问
父类
派生类
实例访问
父类实例
派生类实例
跨模块访问 【√】
import形式导入
from 模块 import * 形式导入
python
复制代码
class Animal:
x = 10
def test(self):
print(Animal.__x)
print(self.__x)
pass
class Dog(Animal):
def test2(self):
print(Dog.x)
print(self.x)
pass
# 测试代码
# 类内部访问
## 父类
a = Animal()
a.test()
# 子类内部访问
d = Dog()
d.test2()
# 模块内其他位置方位
# 类访问
## 父类
print(Animal.x)
## 派生类
print(Dog.x)
# 实例访问
## 父类实例
print(a.x)
## 派生类实例
print(d.x)
a = 666
# 其他模块【另建一个.py文件】
import 私有化属性
print(a)
from 私有化属性 import *
print(a)
_x
受保护属性
类内部访问 【√】
子类内部访问 【√】
模块内其他位置访问 【警告】
类访问
父类
派生类
实例访问
父类实例
派生类实例
跨模块访问
import形式导入 【警告】
from module import * 形式导入
有__all__指明对应变量 【警告】
没有__all__指明对应变量 【会报错】
python
复制代码
class Animal:
_x = 10
def test(self):
print(Animal._x)
print(self._x)
pass
class Dog(Animal):
def test2(self):
print(Dog._x)
print(self._x)
pass
# # 测试代码
# 类内部访问
## 父类
a = Animal()
a.test()
# 子类内部访问
d = Dog()
d.test2()
# 模块内其他位置方位
# 类访问
## 父类
print(Animal._x)
## 派生类
print(Dog._x)
# 实例访问
## 父类实例
print(a._x)
## 派生类实例
print(d._x)
_a = 666
__all__ = ["_a"] # __all__ 列表指定的对象,模块外部可以通过from 模块 import方式导入
# 其他模块【另建一个.py文件】
import 私有化属性
print(a)
from 私有化属性 import *
print(a)
__x
私有属性
类内部访问 【√】
子类内部访问 【X】
模块内其他位置访问 【X】
类访问
父类
派生类
实例访问
父类实例
派生类实例
跨模块访问
参照单下划线开头变量的访问原则
私有属性的实现机制
名字重整(Name Mangling)
重改__x为另外一个名称, 如
_类名__x
目的
防止外界直接访问
防止被子类同名称属性覆盖
应用场景
数据保护
数据过滤
python
复制代码
class Animal:
__x = 10
def test(self):
print(Animal.__x)
print(self.__x)
pass
class Dog(Animal):
def test2(self):
print(Dog.__x)
print(self.__x)
pass
# # 测试代码
# 类内部访问
## 父类
a = Animal()
a.test()
# 子类内部访问
d = Dog()
d.test2() # 会报错
# 模块内其他位置方位
# 类访问
## 父类
print(Animal.__x) # 会报错
## 派生类
print(Dog.__x) # 会报错
# 实例访问
## 父类实例
print(a.__x) # 会报错
## 派生类实例
print(d.__x) # 会报错
__a = 666
__all__ = ["_a"] # __all__ 列表指定的对象,模块外部可以通过from 模块 import方式导入
# 私有属性的实现机制-命名机制:重改__x为另外一个名称, 如_类名__x
# print(Animal.__x) # 会报错
print(Animal.__dict__)
print(Animal._Animal__x) # 成功输出10
# 其他模块【另建一个.py文件】
import 私有化属性
print(私有化属性.__a)
from 私有化属性 import *
print(__a) # 会报错
## 私有化属性的应用场景
```python
class Person:
# 主要作用, 当我们创建好一个实例对象之后, 会自动的调用这个方法, 来初始化这个对象
def __init__(self):
self.__age = 18
def setAge(self, value):
if isinstance(value, int) and 0 < value < 200:
self.__age = value
else:
print("你输入的数据有问题, 请重新输入")
def getAge(self):
return self.__age
p1 = Person()
# p1.__age = -10 # 报错
# print(p1._Person__age) # 不建议
p1.setAge("abc")
# print(p1._Person__age)
print(p1.getAge())
p2 = Person()
p3 = Person()
# print(p1.age)
# print(p2.age)
# print(p3.age)
# print(p1.__dict__)
补充
xx_
"变量名_" 这个格式是为了与系统属性作区分
__xx__
两端__一般为系统内置属性或方法, 所以以后命名注意避免
python
复制代码
# 为了与系统属性作区分
class_
# 系统内置属性或方法,
__xx__
只读属性
概念
一个属性(一般指实例属性), 只能读取, 不能写入
应用场景
有些属性, 只限在内部根据不同场景进行修改, 而对外界来说, 不能修改, 只能读取
比如
电脑类的网速属性, 网络状态属性
方式1
方案
全部隐藏
私有化
既不能读
也不能写
部分公开
公开读的操作
具体实现
私有化
通过"属性前置双下划线"实现
部分公开
通过公开的方法
优化
property
作用
将一些"属性的操作方法"关联到某一个属性中
概念补充
经典类
没有继承(object)
新式类
继承(object)
Python2.x版本定义一个类时, 默认不继承(object)
Python3.x版本定义一个类时, 默认继承(object)
建议使用
新式类
property
在经典类中
只能管理一个属性的读取操作
在新式类中
可以管理一个属性的删改查操作
方式2
方案
借助系统内置的方法进行拦截
具体实现
__setattr__方法
当我们使用 "实例.属性 = 值" 这种格式给一个实例增加或修改属性的时候, 都会调用系统内置的这个方法
在这个方法的内部, 才会真正的把属性以及对应的值给存储到 __dict__当中
解决方案
在这个方法中, 进行拦截
python
复制代码
# 方式一
## 公开方法
class Person(object):
def __init__(self):
self.__age = 18
def getAge(self):
return self.__age
p1 = Person
print(p1.getAge())
## 优化
class Person(object):
def __init__(self):
self.__age = 18
# 主要作用就是, 可以以使用属性的方式, 来使用这个方法
@property
def age(self):
return self.__age
p1 = Person()
print(p1.age)
p1.age = 10 # 报错,不能设置这个属性
# 存储层面修改,是修改不了的
p1._Person__age = 999 # 输出结果:999
p1.__dict__["_Person__age"] = 999 # 输出结果:999
p1.__age = 999 # 新增属性__age = 999
print(p1.__dict__)
## 新式类和经典类
class Person(object):
pass
print(Person.__bases__)
# Python2.x 如果定义一个类, 没有显示的继承自object , 那么这个类就是一个经典类
# 必须显示的继承自, object, 它才是一个新式类
# Python3.x , 如果直接定义一个类, 会隐式的继承object, 默认情况下, 就已经是一个新式类
# propery在新式类和经典类中的使用
# -------------------------------property在新式类中的使用-----------------------------------
class Person(object):
def __init__(self):
self.__age = 18
def get_age(self):
print("----, get")
return self.__age
def set_age(self, value):
print("----, set")
self.__age = value
age = property(get_age, set_age)
p = Person()
print(p.age)
p.age = 90
print(p.age)
print(p.__dict__)
# 第二种使用方式 - 装饰器方法
class Person(object):
def __init__(self):
self.__age = 18
@property
def age(self):
print("----- get")
return self.__age
@age.setter
def age(self, value):
print("----- set")
self.__age = value
p = Person()
print(p.age)
p.age = 10
print(p.age)
# -------------------------------property在经典类中的使用-----------------------------------
class Person:
def __init__(self):
self.__age = 18
def get_age(self):
print("----, get")
return self.__age
def set_age(self, value):
print("----, set")
self.__age = value
age = property(get_age, set_age)
p = Person()
print p.age
p.age = 19 #此方法会新增一个属性,而不是修改实例属性
print p.age
print p.__dict__
# 第二种使用方式-装饰器方法
class Person:
def __init__(self):
self.__age = 18
@property
def age(self):
print "-----get"
return self.__age
@age.setter
def age(self, value):
self.__age = value
p = Person()
# print p.age
p.age = 19
print p.age
print p.__dict__
# 方式二
class Person:
# 当我们通过 "实例.属性 = 值", 给一个实例增加一个属性, 或者说, 修改一下属性值的时候, 都会调用这个方法
# 在这个方法内部, 才会真正的把, 这个属性, 以及对应的数据, 给存储到__dict__字典里面
def __setattr__(self, key, value):
print(key, value)
# 1. 判定, key, 是否是我们要设置的只读属性的名称
if key == "age" and key in self.__dict__.keys():
print("这个属性是只读属性, 不能设置数据")
# 2. 如果说不是, 只读属性的名称, 真正的给它添加到这个实例里面去
else:
# self.key = value # 会报错、陷入死循环
self.__dict__[key] = value
p1 = Person()
p1.age = 18
# p1.name = "sz"
# print(p1.age)
print(p1.age)
p1.age = 999
print(p1.age)
print(p1.__dict__)
内置特殊属性
类属性
__dict__ : 类的属性
__bases__ : 类的所有父类构成元组
__doc__ :类的文档字符串
__name__: 类名
__module__: 类定义所在的模块
实例属性
__dict__ : 实例的属性
__class__: 实例对应的类
python
复制代码
class Person:
"""
这是一个人, 类
"""
age = 19
def __init__(self):
self.name = "sz"
def run(self):
print("run")
# __dict__ : 类的属性
# __bases__ : 类的所有父类构成元组
# __doc__ :类的文档字符串
# __name__: 类名
# __module__: 类定义所在的模块
print(Person.__dict__)
print(Person.__bases__)
print(Person.__doc__)
# help(Person)
print(Person.__name__)
print(Person.__module__)
p = Person()
print(p.__class__)
方法相关补充
私有化方法
私有方法
def __方法():
pass
注意
不要定义 "_类名__方法名" 这种方法
python
复制代码
class Person:
__age = 18
def __run(self):
print("pao")
def _Person__run(self)
print("Xxx")
p = person
# p.__run() #会报错
p.__Person_run() #输出结果Xxx,_Person__run()会覆盖掉run()方法
print(Person.__dict__)
内置特殊方法
生命周期方法(下一小节单独介绍)
其他内置方法
信息格式化操作
__str__方法
作用
一个对象的描述字符串, 更加方便用户阅读, 对用户更友好
触发方式
print 打印一个对象时
str() 函数时
格式
def __str__(self):
return "描述信息"
python
复制代码
class Person:
def __init__(self, n, a):
self.name = n
self.age = a
def __str__(self):
return "这个人的姓名是%s, 这个人的年龄是:%s" % (self.name, self.age)
p1 = Person("sz", 18)
# # print(p1.name)
# # print(p1.age)
print(p1)
p2 = Person("zhangsan", 19)
# # print(p2.name)
# # print(p2.age)
print(p2)
# s = str(p1)
# print(s, type(s))
__repr__方法
作用
一个对象的描述字符串, 更加方便机器处理, 对机器更友好(开发人员查看)
触发方式
当我们在交互模式下, 直接执行某个变量, 就会输出对应信息
repr() 函数时
格式
def __repr__(self):
return "描述信息"
注意
一般情况下, 应满足如下等式
obj == eval(repr(obj))
或者描述一个实例详细的信息(类名等等)
python
复制代码
class Person:
def __init__(self, n, a):
self.name = n
self.age = a
def __str__(self):
return "这个人的姓名是%s, 这个人的年龄是:%s" % (self.name, self.age)
def __repr__(self):
return "reprxxxxx"
p1 = Person("sz", 18)
# # print(p1.name)
# # print(p1.age)
print(p1)
p2 = Person("zhangsan", 19)
# # print(p2.name)
# # print(p2.age)
print(p2)
s = str(p1)
print(s, type(s))
print(repr(p1))
import datetime
t = datetime.datetime.now()
print(t)
print(repr(t))
tmp = repr(t)
result = eval(tmp)
print(result)
调用操作
__call__方法
作用
使得"对象"具备当做函数,来调用的能力
使用
1. 实现实例方法 __call__
2. 那么创建好的实例, 就可以通过函数的形式来调用
实例(参数)
应用场景
有点类似于之前所讲的"偏函数"的应用场景
可以将"常变参数"和"不常变参数"进行分离
案例
不同类型的笔, 画不同的图形
python
复制代码
class Person:
def __call__(self, *args, **kwargs):
print("xxx", args, kwargs)
pass
p = Person()
p(123, 456, name="sz")
# 创建很多个画笔, 画笔的类型(钢笔, 铅笔), 画笔的颜色(红, 黄色, 青色, 绿色)
# 方法一
print("创建了一个%s这个类型的画笔, 它是%s颜色" % ("钢笔", "红色"))
print("创建了一个%s这个类型的画笔, 它是%s颜色" % ("钢笔", "黄色"))
print("创建了一个%s这个类型的画笔, 它是%s颜色" % ("钢笔", "青色"))
print("创建了一个%s这个类型的画笔, 它是%s颜色" % ("钢笔", "绿色"))
print("创建了一个%s这个类型的画笔, 它是%s颜色" % ("钢笔", "橘色"))
# 方法二
def createPen(p_color, p_type):
print("创建了一个%s这个类型的画笔, 它是%s颜色" % (p_type, p_color))
createPen("钢笔", "红色")
createPen("钢笔", "绿色")
createPen("钢笔", "黄色")
# 方法三
import functools
gangbiFunc = functools.partial(createPen, p_type="钢笔")
gangbiFunc("红色")
gangbiFunc("黄色")
gangbiFunc("绿色")
# 方法四
class PenFactory:
def __init__(self, p_type):
self.p_type = p_type
def __call__(self, p_color):
print("创建了一个%s这个类型的画笔, 它是%s颜色" % (self.p_type, p_color))
gangbiF = PenFactory("钢笔")
gangbiF("红色")
gangbiF("绿色")
gangbiF("黄色")
qianbiF = PenFactory("铅笔")
qianbiF("红色")
qianbiF("绿色")
qianbiF("黄色")
索引操作
作用
可以对一个实例对象进行索引操作
步骤
1. 实现三个内置方法
设置元素的方法
def __setitem__(self, key, value):
获取元素的方法
def __getitem__(self, item):
删除元素的方法
def __delitem__(self, key):
2. 可以以索引的形式操作对象
增/改
p[1] = 666
p["name"] = "sz"
查
p["name"]
p[1]
删
del p["name"]
del p[1]
python
复制代码
class Person:
def __init__(self):
self.cache = {}
def __setitem__(self, key, value):
# print("setitem", key, value)
self.cache[key] = value
def __getitem__(self, item):
# print("getitem", item)
return self.cache[item]
def __delitem__(self, key):
# print("delitem", key)
del self.cache[key]
p = Person()
p["name"] = "sz"
print(p["name"])
del p["name"]
print(p["name"])
print(p.cache)
切片操作
作用
可以对一个实例对象进行切片操作
步骤
Python2.x
1. 实现三个内置方法
__setspice__
设置某个元素切片时调用
__getspice__
获取某个元素切片时调用
__delspice__
删除某个元素切片时调用
2. 可以直接按照切片的方式操作对象
p[1, 6, 2]
注意: 过期
Python3.x
统一由"索引操作"进行管理
def __setitem__(self, key, value):
def __getitem__(self, item):
def __delitem__(self, key):
python
复制代码
l = [1, 2, 3, 4, 5]
print(l[3])
print(l[1: 4: 2])
class Person:
def __init__(self):
self.items = [1, 2, 3, 4, 5, 6, 7, 8]
def __setitem__(self, key, value):
# print(key, value)
# print(key.start)
# print(key.stop)
# print(key.step)
# print(value)
# self.items[key] = value
if isinstance(key, slice):
self.items[key.start: key.stop: key.step] = value
def __getitem__(self, item):
print("getitem", item)
def __delitem__(self, key):
print("delitem", key)
p = Person()
p[0: 4: 2] = ["a", "b"]
print(p.items)
# slice
p[0: 5: 2]
del p[0: 5: 2]
比较操作
作用
可以自定义对象 "比较大小, 相等以及真假" 规则
步骤
实现6个方法
相等
__eq__
不相等
__ne__
小于
__lt__
小于或等于
__le__
大于
__gt__
大于或等于
__ge__
注意
如果对于反向操作的比较符, 只定义了其中一个方法但使用的是另外一种比较运算那么, 解释器会采用调换参数的方式进行调用该方法
例如
定义了 "小于" 操作
x < y
使用 x > y
会被调换参数, 调用上面的 "小于操作"
但是, 不支持叠加操作
例如
定义了 "小于" 和 "等于" 操作
不能使用 x <= y
补充
使用装饰器, 自动生成"反向" "组合"的方法
步骤
1. 使用装饰器装饰类
@functools.total_ordering
2. 实现
> 或 >= 或 < 或 <= 其中一个
实现 ==
上下文环境中的布尔值
__bool__
python
复制代码
class Person:
def __init__(self, age, height):
self.age = age
self.height = height
# == != > >= < <=
def __eq__(self, other):
print("eq")
return self.age == other.age
def __ne__(self, other):
pass
def __gt__(self, other):
pass
def __ge__(self, other):
pass
def __lt__(self, other):
# print("lt")
print(self.age)
print(other.age)
return self.age < other.age
def __le__(self, other):
pass
p1 = Person(18, 190)
p2 = Person(19, 190)
print(p1 < p2)
print(p1 <= p2) # p2 < p1
print(p1 == p2)
print(p1 != p2)
print(p1 <= p2) #报错,不支持叠加操作
# ----------------------------比较操作-补充------------------------------------
import functools
@functools.total_ordering
class Person:
def __lt__(self, other):
print("lt")
# pass
return False
def __eq__(self, other):
print("eq")
pass
# def __le__(self, other):
# print("le")
p1 = Person()
p2 = Person()
print(p1 <= p2)
print(Person.__dict__)
# ----------------------------上下文环境的布尔值------------------------------------
class Person:
def __init__(self):
self.age = 20
def __bool__(self):
return self.age >= 18
pass
p = Person()
if p:
print("xx")
遍历操作
怎样让我们自己创建的对象可以使用for in 进行遍历?
实现__getitem__方法
优先级低
每次for in 获取数据时, 都会调用这个方法
或者
实现__iter__方法
优先级高
这个方法, 必须返回一个"迭代器"; 即, 具备"__iter__"和"__next__"方法
当for in 遍历这个对象时, 会调用这个__iter__方法返回的迭代器对象的__next__方法
怎样让我们自己创建的对象可以使用next函数进行访问?
实现__next__方法
补充
1. __iter__方法可以恢复迭代器的初始化值, 复用迭代器
2. "可迭代" 与 "迭代器"必须实现的方法
3. iter方法的使用
python
复制代码
# ----------------------------遍历操作------------------------------------
class Person:
def __init__(self):
self.result = 1
def __getitem__(self, item):
self.result += 1
if self.result >= 6:
raise StopIteration("停止遍历")
return self.result
pass
p = Person()
for i in p:
print(i)
# 方式2
class Person:
def __init__(self):
self.result = 1
def __iter__(self):
print("iter")
self.result = 1
# return iter([1, 2, 3, 4, 5])
return self
def __next__(self):
self.result += 1
if self.result >= 6:
raise StopIteration("停止遍历")
return self.result
p = Person()
# for i in p:
# print(i)
print(next(p))
print(next(p))
print(next(p))
print(next(p))
print(next(p))
print(next(p))
result = next(p)
while result:
print(result)
result = next(p)
# ----------------------------恢复迭代器初始值------------------------------------
class Person:
def __init__(self):
self.age = 1
def __getitem__(self, item):
return 1
def __iter__(self):
self.age = 1 # 此处语句使得迭代器可以使用多次,请思考原因
return self
def __next__(self):
self.age += 1
if self.age >= 6:
raise StopIteration("stop")
return self.age
next()
p = Person()
for i in p:
print(i)
print(p.age)
for i in p:
print(i)
import collections
print(isinstance(p, collections.Iterator))
print(isinstance(p, collections.Iterable))
# ----------------------------iter函数的使用------------------------------------
class Person:
def __init__(self):
self.age = 1
def __getitem__(self, item):
self.age += 1
if self.age >= 6:
raise StopIteration("stop")
return self.age
def __iter__(self):
self.age = 1
return self
def __next__(self):
self.age += 1
if self.age >= 6:
raise StopIteration("stop")
return self.age
def __call__(self, *args, **kwargs):
self.age += 1
if self.age >= 6:
raise StopIteration("stop")
return self.age
p = Person()
pt = iter(p)
pt = iter(p.__next__, 4) # 输出结果:2,3
pt = iter(p, 4) # 输出结果:2,3
print(pt is p)
for i in pt:
print(i)
l = [1, 2, 3]
lt = iter(l)
print(lt)
描述器
概念
可以描述一个属性操作的对象
对象
属性的操作
增/改
删
查
描述
作用
可以代为管理一个类属性的读写删操作, 在相关方法中, 对数据进行验证处理, 过滤处理等等
如果一个类属性被定义为描述器,那么以后对这个类属性的操作(读写删), 都将由这个描述器代理
定义
定义方式1
property
定义方式2
三个方法
__get__
__set__
__delete__
调用细节
使用实例进行调用
最多三个方法都会被调用
使用类进行调用
最多会调用get方法
不能够顺利转换的场景
新式类和经典类
描述器仅在新式类中生效
方法拦截
一个实例属性的正常访问顺序
实例对象自身的__dict__字典
对应类对象的__dict__字典
如果有父类, 会再往上层的__dict__字典中检测
如果没找到, 又定义了__getattr__方法, 就会调用这个方法
而在上述的整个过程当中, 是如何将描述器的__get__方法给嵌入到查找机制当中?
就是通过这个方法进行实现
__getattribute__
内部实现模拟
如果实现了描述器方法__get__就会直接调用
如果没有, 则按照上面的机制去查找
注意
"资料描述器"和"非资料描述器"
如果实现了
_get__
如果实现了
__get__
__set__
描述器和实例属性同名时, 操作的优先级问题
资料描述器 > 实例字典 > 非资料描述器
python
复制代码
# class Person:
# def __init__(self):
# self.__age = 10
# def get_age(self):
# return self.__age)
# def set_age(self,value):
# if value < 0:
# value = 0
# self.__age = value
# def del_age(self):
# del self.__age
# p = Person()
# print(p.age)
# p.set_age = -10
# print(p.get_age())
# p.del_age()
# print(p.get_age())
help(Person)
class Person:
def __init__(self):
self.__age = 10
@property
def age(self):
return self.__age
@age.setter
def age(self, value):
if value < 0:
value = 0
self.__age = value
@age.deleter
def age(self):
print("del age")
del self.__age
# age = property(get_age, set_age, del_age)
# name = "sz"
p = Person()
p.age = 19
del p.age
print(p.age)
# ----------------------------描述器-定义方式2------------------------------------
class Age:
def __get__(self, instance, owner):
print("get")
def __set__(self, instance, value):
print("set")
def __delete__(self, instance):
print("delete")
class Person:
age = Age() # 类属性
def __init__(self):
self.__age = 10
# @property
def get_age(self):
return self.__age
@age.setter
def set_age(self, value):
if value < 0:
value = 0
self.__age = value
@age.deleter
def del_age(self):
print("del age")
del self.__age
age = property(get_age, set_age, del_age)
p = Person()
p.age = 10
print(p.age)
del p.age
# ----------------------------描述器-调用细节------------------------------------
class Age(object):
def __get__(self, instance, owner):
print("get")
def __set__(self, instance, value):
print("set")
def __delete__(self, instance):
print("delete")
class Person(object):
age = Age()
def __getattribute__(self, item):
print "xxxxx"
p = Person()
p.age = 10
print(p.age)
del p.age
print(Person.age)
Person.age = 19
del Person.age
# ----------------------------描述器-和实例属性同名时, 操作优先级------------------------------------
# 资料描述器 get set
# 非资料描述器 仅仅实现了 get 方法, 那么他就是一个非资料描述器
# 资料描述器 > 实例属性 > 非资料描述器
class Age(object):
def __get__(self, instance, owner):
print("get")
def __set__(self, instance, value):
print("set")
def __delete__(self, instance):
print("delete")
class Person(object):
age = Age()
def __init__(self):
self.age = 10
p = Person()
p.age = 10
print(p.age)
del p.age
print(p.__dict__)
# ----------------------------描述器-值的存储问题------------------------------------
class Person:
def __init__(self):
self.__age = 10
def get_age(self):
return self.__age
def set_age(self, value):
if value < 0:
value = 0
self.__age = value
def del_age(self):
print("del age")
del self.__age
age = property(get_age, set_age, del_age)
p = Person()
p.age = 10
print(p.age)
del p.age
class Age(object):
def __get__(self, instance, owner):
print("get")
return instance.v
def __set__(self, instance, value):
print("set", self, instance, value)
instance.v = value
def __delete__(self, instance):
print("delete")
del instance.v
class Person(object):
age = Age()
p = Person()
p.age = 10
print(p.age)
del p.age
p2 = Person()
p2.age = 11
print(p2.age)
print(p.age)
装饰器
使用类当做装饰器来使用
python
复制代码
# ----------------------------使用类, 实现装饰器------------------------------------
# def check(func):
# def inner():
# print("登录验证")
# func()
# return inner
class check:
def __init__(self, func):
self.f = func
def __call__(self, *args, **kwargs):
print("登录验证")
self.f()
@check
def fashuoshuo():
print("发说说")
# fashuoshuo = check(fashuoshuo)
fashuoshuo()
x = "abc"
y = [x]
z = [x, y]
import objgraph
# objgraph.show_refs(y, filename='test.png')
objgraph.show_refs(z, filename="test.png")