类和实例
面向对象编程(OOP) Object Oriented Programming,对象包含了属性和行为。
Class类:用于定义同一类的对象的属性和行为Instance实例:通过Class创建出来的一个个具体的对象
# 定义一个继承于`object`的类
class User(object):
pass
# 通过类创建一个实例
user = User()
类中定义属性和方法
class User(object):
# `__init__` 直接在类中定义属性,创建实例的时候就会自动绑定到对象上
# self 就是实例本身,将 name、age 绑定到 self
def __init__(self, name, age):
self.name = name
self.age = age
# 定义方法,第一个参数必须是 self
def sign(self):
print("sign:",self.name, self.age)
user = User("dawn",18)
# 调用时无需传入 self
print(user.sign())
给实例绑定属性
- 定义类时通过
__init__给实例绑定属性 - 初始化实例后,给实例绑定属性
# 定义类时通过 `__init__` 给实例绑定属性
class User(object):
def __init__(self, name):
self.name = name
# 初始化实例后,给实例绑定属性
user = User()
user.name = "dawn"
print(user.name)
给 类 绑定属性
如果类本身需要绑定一个属性(属性属于类),类属性可以被所有的实例访问,也可以被类本身访问
# 给类绑定属性
class User(object):
name = "dawning"
user = User()
print(user.name) # dawning
print(User.name) # dawning
user.name = "zhangsan"
print(user.name) # zhangsan
给实例绑定一个方法
# 给实例绑定一个方法,给实例绑定的方法不能应用于其他的实例
user.set_age = MethodType(set_age, user)
print(user.set_age(30))
print(user.age) # 30
给类绑定一个方法
# 给类绑定一个方法,给类绑定的方法所有实例都可用
def set_hobby(self, hobby):
self.hobby = hobby
User.set_hobby = set_hobby
user0 = User()
user1 = User()
user0.set_hobby("reading")
user1.set_hobby("writing")
print(user0.hobby)
print(user1.hobby)
__slots__限制 class 实例所能添加的属性
__slots__ 限制实例所能添加的属性
class User(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
user = User()
user.name = "dawn"
user.age = 44
user.phone = '18888888888' # AttributeError
__slots__定义的属性仅对当前类实例起作用,对继承的子类无用;
子类中定义__slots__后,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
class Worker(User):
pass
worker = Worker()
worker.name = "dawn"
worker.age = 44
worker.phone = '18888888888'
@property 将方法变成属性
Python内置的@property装饰器就是负责把一个方法变成一个属性来调用
# birth 和 age 都是 只读 属性
class Worker(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2025 - self._birth
worker = Worker()
worker.birth = 1998
print(worker.age)
获取对象信息
type() 判断对象类型
person = Person("John")
print(type(person))
# 比较两个变量的type类型是否相同
print(type(123) == type(456)) # True
print(type(123) == int) # True
print(type('abc')==str) # True
print(type('abc')==type(123)) # False
types 判断函数类型
判断一个对象是否为函数,可导入 types 模块,使用其定义的常量
import types
def fn():
pass
print(type(fn)==types.FunctionType) # True
print(type(abs)==types.BuiltinFunctionType) #True
print(type(lambda x: x)==types.LambdaType) #True
print(type((x for x in range(10)))==types.GeneratorType) #True
isinstance 判断实例类型
判断对象类型
stu = Student("John", "c001")
print(isinstance(stu, Person)) # True
print(isinstance(stu, Student)) # True
print(isinstance(stu, Teacher)) # False
判断一个变量是否为某个类型中的一种
print(isinstance([1, 2, 3], (list, tuple)))
print(isinstance((1, 2, 3), (list, tuple)))
dir() 获取一个对象的所有属性和方法
print(dir('123'))
运行结果:
['__add__', '__class__', ... ... 'zfill']
getattr()、setattr()以及hasattr(),直接操作对象属性
person = Person("John")
# getattr 获取属性值
print(getattr(person, "userName")) # John
# hasattr 是否具有指定的属性
print(hasattr(person, "password")) # False
# setattr 设置某个属性
setattr(person, "userName", "John1")
print(person.userName) # John1
访问限制
类属性名以__开头,就变成了一个私有属性(private)
class User(object):
def __init__(self, __user_no, name, age):
# 变量名以`__`开头,就变成了一个私有变量(`private`)
self.__user_no = __user_no
self.name = name
self.age = age
user = User("u001", "dawn", 18)
# 私有变量外部调用报错 AttributeError: 'User' object has no attribute
print(user.__user_no)
增加get方法访问私有属性
class User(object):
... ...
def get_user_no(self):
return self.__user_no
user = User("u001", "dawn", 18)
# 调用get方法访问私有属性
print(user.get_user_no())
-
类属性定义为
__xxx__是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__age__这样的变量名。 -
一个下划线开头的属性,
_xxx,这样的实例变量外部是可以访问的,但是尽量不可随意访问。
class User(object):
def __init__(self, __id__, __user_no, _phone, name, age):
# 属性命名格式为 __xx__ 不建议,这种命名为关键字
self.__id__ = __id__
# 外部是可以访问的,但是尽量不可随意访问
self._phone = _phone
self.name = name
self.age = age
def get_id(self):
return self.__id__
user = User(1, "u001", "17777777777", "dawn", 18)
print(user.get_id())
print(user._phone)
实际上,我们也可以访问 __xxx 这样的变量因为python解释器会将 __xxx 这样的变量,改成 _类名__xxx
属性中定义的 __xxx 属性就是 __xxx 解释器不会修改
class User(object):
def __init__(self, __user_no):
self.__user_no = __user_no
user = User("u001")
user.__user_no = "c001"
# 外部通过_类名+属性名访问
print(user._User__user_no)
# 并非和 class 中定义的 __user_no 是同一个变量
# class 中定义的 __user_no 已经被修改成了 _User__user_no
print(user.__user_no)
继承和多态
extends
Student 和 Teacher 继承于 People 类
class Person:
def __init__(self, userName):
self.userName = userName
def working(self):
print(self.userName, " working")
class Student(Person):
def __init__(self, userName, classNo):
super().__init__(userName)
self.classNo = classNo
class Teacher(Person):
def __init__(self, userName, teacherNo):
super().__init__(userName)
self.teacherNo = teacherNo
student = Student("John", 1)
student.working()
teacher = Teacher("HuaPeo", 2)
teacher.working()
# isinstance 可以判断是否属于某个类型
print(isinstance(teacher, Teacher))
file-like object 鸭子类型
python 并不要求严格的继承体系,file-like object
class Worker(object):
def __init__(self, userName):
self.userName = userName
def working(self):
print(self.userName, " working")
# 传入的Worker对象并没有继承Person类,但具有running方法
worker0 = Person(Worker("Hew"))
worker0.running()
多重继承
一个子类就可以同时获得多个父类的所有功能,选择组合不同的类的功能来快速构造出一个新的子类
class UDPMixIn(object):
pass
class ForkingMixIn(object):
pass
class Server(UDPMixIn, ForkingMixIn):
pass
定制类
Python中在运行期间可以创建属性、对象、函数等,这样就会很方便的定制特定的类。
如同__xxx__的变量或者函数名比如,__len__()、__slots__,在Python中是有特定用途,可以帮我们定特定的类。
__str__ 定制打印对象
# 默认的对象打印
class User:
def __init__(self, name):
self.name = name
user = User("John")
print(user) # <__main__.User object at 0x105cc8ef0>
# `__str__ ` 定制对象打印
class User:
def __init__(self, name):
self.name = name
def __str__(self):
return 'User[name=%s]' % self.name
user = User("John")
print(user) # User[name=John]
__iter__ 获取迭代对象
类似list或tuple,想让一个类对象使用for-in循环,就必须实现一个__iter__()并返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
class EvenNumberAcquirer(object):
def __init__(self, maxNumber):
self.a = 0
self.maxNumber = maxNumber
def __iter__(self):
return self # 实例本身就是迭代对象,直接返回自己
def __next__(self):
if self.a > self.maxNumber:
raise StopIteration()
if self.a % 2 == 0:
return_val = self.a
self.a += 1
return return_val
else:
return_val = self.a + 1
self.a += 2
return return_val
for n in EvenNumberAcquirer(100):
print(n)
__getitem__ 获取元素
根据下标获取元素
class EvenNumberAcquirer(object):
... ...
# __getitem__ 根据下标获取元素
def __getitem__(self, item):
return item * 2
even_number = EvenNumberAcquirer(100)
print(even_number[0]) # 0
print(even_number[7]) # 14
实现切片[x:y]操作
class EvenNumberAcquirer(object):
... ...
# isinstance 判断item的类型,返回不同的元素
def __getitem__(self, item):
if isinstance(item, int):
return item * 2
if isinstance(item, slice):
start = item.start
if item.start is None:
start = 0
L = []
for x in range(start,item.stop):
L.append(x * 2)
return L
even_number = EvenNumberAcquirer(100)
print(even_number[1:7])
如果要实现[1:7:2]、[-10:] 等这样的切片操作,都需要自己去实现。
如果把对象看成dict
__getitem__()获取某个key对应的元素__setitem__()设置某个key对应的元素__delitem__()删除某个元素。
class MyDict:
def __init__(self):
self.data = {}
# __getitem__:用于获取元素
def __getitem__(self, key):
print(f"Getting the value for key: {key}")
return self.data[key]
# __setitem__:用于设置元素
def __setitem__(self, key, value):
print(f"Setting key {key} to value {value}")
self.data[key] = value
# __delitem__:用于删除元素
def __delitem__(self, key):
print(f"Deleting the key: {key}")
del self.data[key]
my_dict = MyDict()
# 使用 __setitem__ 来设置键值对
my_dict["name"] = "Alice"
my_dict["age"] = 25
# 使用 __getitem__ 来获取值
print(my_dict["name"]) # 输出: Alice
# 使用 __delitem__ 来删除键值对
del my_dict["age"]
__getattr__ 动态获取元素属性
访问一个不存在的属性会报错
class User:
pass
user = User()
print(user.name) # AttributeError
__getattr__ 动态返回对应值
class User(object):
def __getattr__(self, attr):
if attr=='name':
return '王林'
user = User()
print(user.name)
__getattr__ 也可以动态返回一个方法
class User(object):
def __getattr__(self, attr):
if attr=='get_name':
return lambda :"王林"
user = User()
print(user.get_name())
实现一个 URL 的动态拼接
class Url(object):
def __init__(self, path = ""):
self.path = path
def __getattr__(self, path):
return Url('%s/%s' % (self.path, path))
# 支持调用括号的方式
def __call__(self, *args):
# 将数字(或其他参数)作为路径的一部分
if args:
return Url(f"{self.path}/{args[0]}")
return self
def __str__(self):
return self.path
__repr__ = __str__
print(Url().app.api(1).user.page) # /app/api/1/user/page
print(Url().app.api.user.page) # /app/api/user/page
__call__ 直接调用实例
调用实例方法时可通过 instance.method()来调用,而调用实例时可通过 instance() 来调用,但是调用实例需要定义 __call__方法
class User(object):
def __call__(self, *args, **kwargs):
print(f'call args:{args} kwargs:{kwargs}')
user = User()
user(1,2,3,name="婉儿",age=2000) # call args:(1, 2, 3) kwargs:{'name': '婉儿', 'age': 2000}
使用枚举类
from enum import Enum, unique
TimeUnit = Enum('Time', ('YEAR', 'MONTH', 'DAY', 'HOUR', 'MINUTE', 'SECOND'))
for name, member in TimeUnit.__members__.items():
print(name, '=>', member, '=>', member.value)
从Enum 派生出自定义的类,可精确的控制枚举类型
@unique # @unique装饰器可以帮助我们检查保证没有重复的值
class TimeUnit(Enum):
YEAR = 1
MONTH = 2
DAY = 3
HOUR = 4
MINUTE = 5
SECOND = 6
访问枚举对象
year = TimeUnit.YEAR
print(year) # TimeUnit.YEAR
print(year == TimeUnit.SECOND) # False
print(year.value) # 1
print(TimeUnit(2)) # TimeUnit.MONTH
使用元类
type() 动态创建类
class 的类型为 type,instance 的类型为class type
type() 既可以返回一个对象类型又可以创建一个新的类
# type() 返回一个对象类型
class User:
pass
user = User()
print(type(User)) # <class 'type'>
print(type(user)) # <class '__main__.User'>
创建类的方式:
class:定义类(常用)type():可动态创建出类(动态语言本身支持运行期动态创建类)
class Working:
pass
class Running:
pass
def reading(self, name):
print(f'The reading book is {name}')
# type() 创建一个 User 类
"""
第一个参数:类名
第二个参数:继承的父类
第三个参数:函数绑定
"""
User = type('User', (Working, Running), dict(reading=reading))
print(type(User))
user = User()
user.reading('<仙逆>') # The reading book is <仙逆>
metaclass 创建或修改类
metaclass是Python的魔术对象,它可以改变类创建时的行为。
创建实例的方式
- 定义类=>创建实例
- 定义元类=>定义类=>创建实例
# ListMetaclass(命名一般XxxMetaclass)
class ListMetaclass(type):
"""
第一个参数:要创建的类对象(MyListMetaclass 类对象)
第二个参数:要创建的类名 (MyListMetaclass)
第三个参数:类继承的父类集合 (list)
第四个参数:一个字典,包含即将创建的类的所有属性和方法
"""
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, val: self.append(val)
return type.__new__(cls, name, bases, attrs)
class MyListMetaclass(list, metaclass=ListMetaclass):
pass
list0 = MyListMetaclass()
list0.add('王林')
list0.add('婉儿')
print(list0) # ['王林', '婉儿']
元类示例
利用元类,打印sql
# 定义列(列名,列类型)
class Column(object):
def __init__(self, cname, ctype):
self.cname = cname
self.ctype = ctype
def __str__(self):
return '<%s:%s:%s>' % (self.__class__.__name__, self.cname, self.ctype)
# 定义String类型的列
class StringColumn(Column):
def __init__(self, name):
super(StringColumn, self).__init__(name, 'varchar(100)')
# 定义Integer类型的列
class IntegerColumn(Column):
def __init__(self, name):
super(IntegerColumn, self).__init__(name, 'bigint')
# 定义 Metaclass 从指定类型中获取所有属性并将其添加至 mappings 中
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
# 如果创建的类为 Model 类直接返回
if name == 'Model':
return type.__new__(cls, name, bases, attrs)
print('Found model: %s' % name)
mappings = dict()
# 获取所有的属性,并添加到 mappings 中
for k, v in attrs.items():
if isinstance(v, Column):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v
for k in mappings.keys():
attrs.pop(k)
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
attrs['__table__'] = name # 假设表名和类名一致
return type.__new__(cls, name, bases, attrs)
class Model(dict, metaclass=ModelMetaclass):
def __init__(self, **kw):
super(Model, self).__init__(**kw)
# self 本身是一个dict 动态获取属性实例
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
# 动态给当前实例设置属性
def __setattr__(self, key, value):
self[key] = value
def print_sql(self):
columns = []
args = []
# ModelMetaclass 在创建类对象时,会获取当前对象的所有属性并设置到 mappings 中
for k, v in self.__mappings__.items():
columns.append(v.cname)
args.append(getattr(self, k, None))
sql = "insert into %s (%s) values (%s)" % (self.__table__, ','.join(columns), ','.join(['?'] * len(columns))),
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))
class User(Model):
id = IntegerColumn('id')
name = StringColumn('name')
birthday = StringColumn('birthday')
phone = StringColumn('phone')
u = User(id=111, name='王林', birthday='2025-02-15', phone='7777777')
u.print_sql()
# 执行结果
Found model: User
Found mapping: id ==> <IntegerColumn:id:bigint>
Found mapping: name ==> <StringColumn:name:varchar(100)>
Found mapping: birthday ==> <StringColumn:birthday:varchar(100)>
Found mapping: phone ==> <StringColumn:phone:varchar(100)>
SQL: insert into User (id,name,birthday,phone) values (?,?,?,?)
ARGS: [111, '王林', '2025-02-15', '7777777']