面向对象
对象支持函数调用
示例:
def hello():
print('Hello world!')
hello()
a = 250
# callable() 函数用于检查一个对象是否是可调用的
print(callable(a))
class Person:
# 将对象当做函数调用时会自动触发
def __call__(self, *args, **kwargs):
print('__call__')
print(args)
print(kwargs)
p = Person()
p(1, 2, 3, name='dahua', age=18)
# 判断是否可调用
# print(callable(p))
# 判断是否有call属性
print(hasattr(p, '__call__'))
# 判断是否是函数
from inspect import isfunction
print(isfunction(p))
抽象基类(了解)
说明:
抽象基类的作用不是为了创建对象(实例化)的
抽象基类是为了统一接口而存在的
示例:
注意 abc 是一个库 不要再 命名的时候 随意命名成 abc 而出现问题
抽象方法:子类中必须实现,所有可以统一接口
from abc import ABC, abstractmethod
# 抽象基类
class Animal(ABC):
# 定义抽象方法:子类中必须实现,所有可以统一接口
@abstractmethod
def run(self):
pass
# a = Animal()
# 子类必须实现基类中的抽象方法,否则无法实例化
class Dog(Animal):
def run(self):
print('狗喜欢疯跑')
d = Dog()
class Cat(Animal):
def run(self):
print('猫喜欢走猫步')
c = Cat()
魔术方法
回顾:
魔术方法 触发时机
__ str __ 打印 或 转换为字符串
__ init __ 构造方法,创建对象后初始化时
__ del __ 析构方法,对象即将销毁时
__ setattr __ 设置属性
__ getattr __ 获取不存在的属性
__ delattr __ 删除属性
__ setitem __ 当做字典操作,设置属性
__ getitem __ 当做字典操作,获取属性
__ delitem __ 当做字典操作,删除属性
__ call __ 将对象当做函数调用时
repr
a = 10
b = 20
# 会执行有效的python代码字符串
c = eval('a + b')
print(c)
d = eval("{'name': 'dahua', 'age': 18}")
print(d, type(d))
# 可以得到对象创建的字符串表示形式
# repr() 函数将对象转化为供解释器读取的形式。
s = repr(d)
print(s, type(s))
# 自定义对象
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 对象使用repr函数处理时会自动触发,打印对象(str不存在)时也会触发
# 通常返回对象创建的字符串表示形式
def __repr__(self):
return "Person('{}', {})".format(self.name, self.age)
# 打印对象时:首先找__str__,然后找__repr__,最后打印 类名+地址
def __str__(self):
return 'name:{} age:{}'.format(self.name, self.age)
xiaoming = Person('xiaoming', 18)
s = repr(xiaoming)
print(s)
xm2 = eval(s)
print(xm2)
算术运算符重载
class Number:
def __init__(self, num):
self.num = num
# 当对象出现在 '+' 的左边时会自动触发
def __add__(self, other):
print('__add__')
return self.num + other
# 当对象出现在 '+' 的右边时会自动触发
def __radd__(self, other):
print('__radd__')
return self.num + other
# '+='运算时会自动触发,若没有实现则会尝试__add__
def __iadd__(self, other):
# print('__iadd__')
self.num += other
return self
def __str__(self):
return 'num:{}'.format(self.num)
n = Number(10)
# ret = n + 20
# ret = 20 + n
# print(ret)
n += 20
print(n)
关系运算符重载
class Number:
def __init__(self, num):
self.num = num
# 大于:>
def __gt__(self, other):
print('__gt__')
return self.num > other
# 大于等于:>=
def __ge__(self, other):
print('__ge__')
return self.num >= other
# 小于:<
def __lt__(self, other):
print('__lt__')
return self.num < other
# 小于等于:<=
def __le__(self, other):
print('__le__')
return self.num <= other
# 等于:==
def __eq__(self, other):
print('__eq__')
return self.num == other
# 不等于:!=,当没写时会尝试__eq__
def __ne__(self, other):
print('__ne__')
return self.num != other
n = Number(5)
# 大于
# print(n > 4)
# print(4 < n)
# 小于
# print(n < 4)
# print(4 > n)
# print(n == 6)
print(n != 6)
__new__方法
class Person(object):
# 用于创建对象
def __new__(cls, *args, **kwargs):
print('__new__')
# 创建对象,然后返回
return object.__new__(cls)
# 用于初始化对象
def __init__(self):
print('__init__')
# p = Person.new(Person)
# p.init()
# 相当于执行了上面两句(创建对象+初始化)
# p = Person()
p1 = Person()
p2 = Person()
print(id(p1))
print(id(p2))
设计模式
说明:针对特定场景问题的特定解决方案
单例模式:无论创建多少次,得到的对象始终是同一个
示例:
class Person(object):
def __new__(cls, *args, **kwargs):
# 判断当前类是否拥有_instance属性
if not hasattr(cls, '_instance'):
# 只有第一次创建才会出现 后面进行创建的话 都是同一个
print('创建对象')
cls._instance = object.__new__(cls)
return cls._instance
p1 = Person()
p2 = Person()
print(id(p1))
print(id(p2))
属性函数
说明:将成员方法当做属性一样访问
作用:可以干预指定属性的获取及设置操作
示例:
class User:
def __init__(self, username, password):
self.username = username
self.password = password
# 获取器:获取password属性时自动触发 相当于将passwordb保护起来 返回给用户 我们想让他看见的
@property
def password(self):
print('你想干啥?')
return '偷看密码,不可能'
# 设置器:设置password属性时自动触发
@password.setter
def password(self, password):
print('设置密码', password)
self.__dict__['password'] = 'xxx' + password + 'yyy'
user = User('xiaoming', '123456')
print(user.password)
# user.password = '654321'
print(user.__dict__)
内存管理
非常经典
概念:保证不再使用的对象得到合理及时的释放
引用计数:
当创建一个对象赋值给一个变量时,将引用计数记为1;当多一个变量指向该对象,引用计数加1;当少一个变量指向该对象时,引用计数减1,减到0时释放对象,顺便回调用对象的__del__方法释放相关的资源
说明:引用计数是对可变变量及自定义对象而言的,不可变变量对于我们来说没有研究的意义
示例:
a = 10
b = 10
print(id(a))
print(id(b))
from sys import getrefcount
# 不可变变量的引用计数没有意义
print(getrefcount(a))
lt = [1, 2, 3]
lt2 = lt
# 函数本身会增加1次引用计数
print(getrefcount(lt))
del lt2
print(getrefcount(lt))
class Person:
def __del__(self):
print('对象即将释放')
p = Person()
print(getrefcount(p))
del p
print('OVER')
引用传递
def test(n):
n += 1
# 不可变变量传递的是值
num = 10
# test(num)
# test(n=num)
# test(n=10)
test(10)
print(num)
def demo(lt):
lt[0] = 100
# 可变变量传递的引用
l = [1, 2, 3]
demo(lt=l)
print(l)
# 100 2 3
# 默认值是可变变量,永远都是同一个
def fun(lt=[]):
print(id(lt))
lt.append('abc')
return lt
print(fun())
print(fun([]))
print(fun())
生成的是一个算法 每一个列表的元素都是一个函数 但是没有执行
lt = [lambda: x*x for x in range(1, 10)]
print(type(lt[0]))
在这个时候才开始执行
每一个算法中都会进行循环到9
print(lt0)
print(lt1)
print(lt2)