一、类型判断
1.1、isinstance
# 类型判断
class Foo: pass
hello = "world"
print(isinstance(hello, str)) # 各种类型的判断, str,dict
print(isinstance(123, int)) # True
f1 = Foo()
print(isinstance(f1, Foo)) # True #判断 f1是否为Foo生成的对象
print(isinstance(f1, str)) # False # 非str类型,而是Foo的对象
1.2、issubclass
# 查看运行的类 属不属于这个类的子类
class Base: pass
class Foo(Base): pass
class C3(Foo): pass
# 判断Foo是不是Base的子类
print(issubclass(Foo, Base)) # True
print(issubclass(Base, Foo )) # False
print(issubclass(C3, Base )) # True
print(C3.__mro__)
# (<class '__main__.C3'>, <class '__main__.Foo'>, <class '__main__.Base'>, <class 'object'>)
1.3、type
# 查看实例化对象的类
class Foo(): pass
obj = Foo()
# 查看类
print(obj, type(obj)) # 查看obj的类 <class '__main__.Foo'>
二、反射
什么是反射: 通过字符串来操作类或者对象的属性
class Hello:
hna = "xion"
def world(self):
print("world")
# 生成对象
h1 = Hello()
h1.name = "xiong" # 添加一个对象属性
2.1、hasattr、判断类里是否有这个属性
# 查询的时候不管是 数据属性还是类, 都是以字符串的形式进行查询
print(hasattr(h1, "world")) # True, 判断对象类是否存在
print(hasattr(h1, "World")) # False
print(hasattr(h1, "name")) # True 判断数据属性是否存在
print(hasattr(h1, "xxxx")) # False
print(hasattr(Hello, "world")) # 判断 类函数是否存在
print(hasattr(Hello, "hna")) # 类属性是否存在
2.2、getattr、获取类中是否有这个属性
# 判断对象中是否有这个类
print(getattr(h1, "world")) # <bound method Hello.world of <__main__.Hello object at 0x0000018C9FE9CAC8>>
print(getattr(h1, "world1", None)) # None ,如果不存在返回默认值
# 获取绑定对象,
test = getattr(h1, "world")
test() # 执行绑定对象,world
print(getattr(h1, 's11')) # 没有定义就会直接报 AttributeError 没有值的错误
2.3、setattr、设置一个属性
# 需要注意的是 不管是哪一个反射属性, 第二个属性值都是字符串 " ", 等于是 h1.age = 10000
setattr(h1, "age", 10000) # setattr(x, 'y', v) is equivalent to ``x.y = v''
print(h1.__dict__) # {'name': 'xiong', 'age': 10000}
2.4、delattr、删除数据属性或函数
# 删除对象属性, 等于是 del h1.age
delattr(h1, "age") # delattr(x, 'y') is equivalent to ``del x.y''
print(h1.__dict__) # {'name': 'xiong'}
三、内置方法
__str__
在对象self被打印时,自动触发,应该在该方法内采集与对象self有关的信息,然后拼成字符串返回
class Human(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return str(self.__dict__) # # 返回的结果需要是字符串格式
# 没有定义__str__时会直接 返回内存地址
hu = Human("小白", 1000) # <__main__.Human object at 0x00000202EDD7CA20>
print(hu, type(hu)) # {'name': '小白', 'age': 1000} <class '__main__.Human'>
x = json.dumps(str(hu)).encode("utf-8")
print(json.loads(x), type(json.loads(x))) # {'name': '小白', 'age': 1000} <class 'str'>
_del_
析构方法,当对象在内存中被释放时,自动触发执行, 当程序都结束之后,自动回收对象
class files:
def __init__(self, name):
self.name = name
self.f = open("a.txt","rt", encoding="utf-8")
def __del__(self):
# 如果 对象即占用系统资源又占用应用程序资源就应该被定义回收
self.f.close()
# 注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
__init__
类实例的初始化操作 , 它其实不是第一个被调用的方法,最先被调用的是__new__方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name
p=Person('xiong', 50)
print(p) # 返回 __str__方法
__repr__
python解释器环境下,会默认显示对象的repr表示
class S2:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return "姓名: {0} 年龄: {1}".format(self.name, self.age)
str函数或者print函数调用的是obj.__str__()
repr函数或者交互式解释器调用的是obj.__repr__()
注意:
如果__str__没有被定义,那么就会使用__repr__来代替输出。
__str__和__repr__方法的返回值都必须是字符串。
_format_
格式化字符串, 实际没啥用, 可以在 return 的时候直接固定格式就行
class S3:
def __init__(self, name, age):
self.name = name
self.age = age
times = {
'n-a': '名字是:{obj.name} - 年龄是:{obj.age}', # 名字是:lqz-年龄是:18
'n:a': '名字是:{obj.name} : 年龄是:{obj.age}', # 名字是:lqz:年龄是:18
'n/a': '名字是:{obj.name} / 年龄是:{obj.age}', # 名字是:/年龄是:18
}
def __format__(self, format_spec):
# 如果没有定义 format_spec 或者 定义的不在 times 字典中就用 n-a的方式
if not format_spec or format_spec not in self.times:
format_spec = "n-a"
fmt = self.times[format_spec]
print(fmt)
return fmt.format(obj=self)
s3 = S3('xiong', '19')
ret = format(s3, 'n-a')
print(ret)
_dict_
Python中的类,都会从object里继承一个__dict__属性,这个属性中存放着类的属性和方法对应的键值对。一个类实例化之后,这个类的实例也具有这么一个__dict__属性。但是二者并不相同
class f1(object):
'''
测试 __doc__
'''
name = 'xiong'
def __init__(self, num):
self.num = num
f=f1('11')
print(f.__dict__) # 实例化的字典 {'num': '11'}
print(f1.__dict__) # f1类全局的字典
_slots_
尽管__slots__看起来是个非常有用的特性,但是除非你十分确切的知道要使用它,否则尽量不要使用它。比如定义了__slots__属性的类就不支持多继承。__slots__通常都是作为一种优化工具来使用
class F1(object):
__slots__ = {
'name': 'xx'}
f = F1()
f.name = "xiong" # 固定死的格式
print(f.__dict__) # AttributeError: 'F1' object has no attribute '__dict__'
print(f.__slots__) # {'name': 'xx'}
__item__系列
设置 实例[key] = value 就是调用的 item方法
class F1(object):
def __init__(self, name):
self.name = name
def __getitem__(self, item):
print('getitem: {}'.format(self.__dict__[item]))
def __setitem__(self, key, value):
print("__setitem__")
self.__dict__[key] = value
def __delitem__(self, key):
print('__delitem__')
self.__dict__.pop(key)
f = F1('xiong') # 实例化类
f["age"] = 100 # __setitem__的使用方法
f['xx'] # __getitem__
del f['age'] # 执行的是 __delitem__
__attr__系列
设置 实例[key] = value 就是调用的 attr 方法
__getattr__:当使用点号获取实例属性时,如果属性不存在就自动调用__getattr__方法。
__setattr__:当设置类实例属性时自动调用,如 f.name=5 就会调用__setattr__方法 。
class F1(object):
def __init__(self, name):
self.name = name
def __getattr__(self, item):
print('__getattr__')
def __setattr__(self, key, value):
print('__setattr__')
self.__dict__[key] = value
def __delattr__(self, item):
print("__delattr__")
self.__dict__.pop(item)
f = F1('xiong') # 实例化类
f.xx = 100 # __setattr__
f.xxxxx # 属性不存在就会调用 __getattr__
del f.xx # 执行的是 __delattr__
print("f.xx: ", f.__dict__)
__iter__和__next__
如果一个对象拥有了__iter__和__next__方法,那这个对象就是可迭代对象 比如 yield
__enter__和__exit__
一个对象如果实现了__enter__和__exit__一个对象如果实现了__enter__和__exit__方法,那么这个对象就支持上下文管理协议,即with语句
四、元类 exec
什么是元类, 博客
# 在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象,
# 负责产生该对象的类称之为元类,即元类可以简称为类的类
# 示例
class Foo:pass # 等于 Foo=元类()
为何要用元类
元类是负责产生类的,所以我们学习元类或者自定义元类的目的, 是为了控制类的产生过程,还可以控制对象的产生过程
创建元类, 函数的方法
# 元类
cmd='''
a=1
b=2
def fun():
c=3
'''
local_dir={
}
# exec, 模拟代码并执行,模拟类到名称空间的过程
exec(cmd,{
},local_dir)
print(local_dir) # {'a': 1, 'b': 2, 'fun': <function fun at 0x0000000001EB89D8>}
################# 相当于是 ####################
class Adic:
a=1
b=2
def fun(self):
c=3
a=Adic
print(a.__dict__)
# 'a': 1, 'b': 2, 'fun': <function Adic.fun at 0x0000000001EB8A60>
4.1、创建类的方法有两种
大前提:如果说类也是对象的话,那么用class关键字的去创建类的过程也是一个实例化的过程
该实例化的目的是为了得到一个类,调用的是元类
方式一:使用默认的元类type
class Anm:
def __init__(self, name):
self.name = name
def haha(self):
print(self.name)
print(type(Anm)) # 默认元类 <class 'type'>
创建类的3个要素:类名,基类,类的名称空间
class_name = "Anms" # 类名
class_object = (object,) # 基类, 也就是父类, 这里需要是一个元组
class_dic = {
} # 自定义一个名称空间
# 需要注意的是 这里要顶格写
class_body = """
def __init__(self, name):
self.name = name
def haha(self):
print(self.name)
"""
exec(class_body, {
}, class_dic)
# print(class_dic)
# {'__init__': <function __init__ at 0x00000000021F89D8>, 'haha': <function haha at 0x00000000021F8AE8>}
an1 = type(class_name,class_object, class_dic)
print(an1)
print(Anm)
# <class '__main__.Anms'>
# <class '__main__.Anm'>
方式二:用的自定义的元类
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
def __init__(self,class_name,class_bases,class_dic):
print(self) #现在是People
print(class_name)
print(class_bases)
print(class_dic)
super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父类的功能
# 分析用class自定义类的运行原理(而非元类的的运行原理):
#1、拿到一个字符串格式的类名class_name='People'
#2、拿到一个类的基类们class_bases=(obejct,)
#3、执行类体代码,拿到一个类的名称空间class_dic={...}
#4、调用People=type(class_name,class_bases,class_dic)
class People(object,metaclass=Mymeta): #People=Mymeta(类名,基类(x,y,z,),类的名称空间)
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
def eat(self):
print('%s is eating' %self.name)
产生过程:
__call__
__call__执行是由对象后加括号触发的,即:对象()。拥有此方法的对象可以像函数一样被调用
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __call__(self, *args, **kwargs):
print('调用对象的__call__方法')
p= Person('xiong', 123)
# 实例化p 加上括号就是 执行 __call__方法
@ 注意:__new__、__init__、__call__等方法都不是必须写的。
应用,自定义元类控制的产生过程,类的产生过程其实就是元类的调用过程
# 需要有 类名, 基类, 名称空间
class Mytype(type):
def __init__(self, class_name, class_object, class_dic):
# print(class_dic.get("__doc__")) # None 如果people啥都没有定义 那就打印为none
if class_dic.get("__doc__") is None or len(class_dic.get("__doc__").strip()) == 0:
raise TypeError("类必须要有注释")
if not class_name.istitle():
raise TypeError("类名首字母必须大写")
super(Mytype, self).__init__(class_name, class_object, class_dic)
class People(object, metaclass=Mytype):
'''1'''
def __init__(self, name):
self.name = name
def haha(self):
print(self.name)
p1=People("xiong")
print(People.__dict__) # '__doc__': None
__new__
使用 类名()
创建对象时,python的解释器首先会调用 __new__方法为对象 分配空间,类被执行的时候, 第一个运行的就是__new__, __new__是一个由 objece基类提供的静态方法,其主要作用有两个
- 先造出一个类的空对象 如 obj=self.__new__(self)
- 为该空对象初始化独有属性 self.__init__(obj, *args, **kwargs)
- 返回 该 对象 return obj
__new__方法在类定义中不是必须写的,如果没定义的话默认会调用object.__new__去创建一个对象, 重写 __new__方法一定要 return super().__new__(cls)
,否则python的解释器得不到 分配空间的对象引用 ,就不会调用对象的初始化方法
注意: __new__是一个静态方法,在调用时需要 主动传递cls
参数
class Person:
# 如果我们在类中定义了__new__方法,就是重写了默认的__new__方法,我们可以借此自定义创建对象的行为。
def __new__(cls, *args, **kwargs):
print('调用__new__,创建类实例')
return super().__new__(Person)
def __init__(self, name, age):
print("调用__init__")
self.name = name
self.age = age
def __str__(self):
return self.name
p=Person('xiong', 50)
print(p) # 返回 __str__方法
'''
调用__new__,创建类实例
调用__init__
xiong
'''
示例
class Myfoo(type):
def __call__(self, *args, **kwargs):
# print(self) # <class '__main__.Foo'>
# print(args)
# print(kwargs)
# 1、创建一个空对象
obj = self.__new__(self)
# 2、为该空对象初始化独有的属性
self.__init__(obj, *args, **kwargs)
print(obj.__dict__)
return obj
class Foo(object, metaclass=Myfoo):
def __init__(self, name, age):
self.name = name
self.age = age
def __new__(cls, *args, **kwargs):
print(cls) # <class '__main__.Foo'>
print(args) #
print(kwargs)
return super(Foo, cls).__new__(cls)
class Person(type):
def __init__(self, cls_name, cls_obj, cls_dir):
# 控制类 Foo 的创建
super(Person, self).__init__(cls_name, cls_obj, cls_dir)
def __call__(self, *args, **kwargs):
# 控制 FOO 类的调用过程,即foo对象的产生过程 obj=Foo("hhh",111)
obj = self.__new__(self) # 创建一个空对象
self.__init__(obj, *args, **kwargs) # 为对象创建初始化其独有的属性
return obj
class Foo(object, metaclass=Person):
def __init__(self, name, age):
self.name = name
self.age = age
4.2、单例模式
# 1、什么是单例模式
# 大白话: 实例N次,最终得到一个对象名称内存地址
# 基于某种方法实例化多次得到的实例是同一个, 在系统中只有唯一的一个实例,每一次执行 "类名()" 返回的对象,内存地址是相同的
# 2、什么情况下会使用?
# 如: 初始化数据库连接
当实例化多次得到的对象中存放的属性都一样的情况,应该将多个对象指向同一个内存地址
示例 -1, 通过类属性重复调用属性
# conifure.ini 内容如下
[mysql]
host=127.0.0.1
port=3306
import configparser
class Mysql(object):
__instance = None # 通过判断类属性的方法来重复调用一个名称空间地址
def __init__(self, host, port):
self.host = host
self.port = port
@classmethod
def from_conf(cls):
if cls.__instance is None:
conf = configparser.ConfigParser()
conf.read("configure.conf")
cls.__instance = Mysql(conf.get("mysql", "host"), conf.get("mysql", "port"))
return cls.__instance
sql1 = Mysql.from_conf()
sql2 = Mysql.from_conf()
sql3 = Mysql.from_conf()
# 调用的名称空间都是同一个,其类对象返回的名称空间地址也是同一个
示例2-装饰器
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
from functools import wraps
import configparser
config = configparser.ConfigParser()
config.read("configure.conf")
def setting(obj):
_instance = obj(config.get("mysql", "host"), config.get("mysql", "port"))
@wraps(obj)
def wrapper(*args, **kwargs):
if len(args) == 0 and len(kwargs) == 0:
return _instance
return obj(*args, **kwargs)
return wrapper
@setting
class Foo(object):
def __init__(self, host, port):
self.host = host
self.port = port
m1 = Foo()
m2 = Foo()
print(m1)
print(m2)
f = Foo("1.1.1.1", 2000)
print(f)
# 1、@setting 将Foo通过装饰器传递,判断如果 类传递的 位置参数,关键字参数
# 2、如果参数都为空,直接返回 默认的属性, obj(这里直接传递的就是args值)
示例3 - 元类
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
class Mymeta(type):
def __init__(self, cls_name, cls_obj, cls_dic):
super(Mymeta, self).__init__(cls_name, cls_obj, cls_dic)
obj = self.__new__(self) # 创建一个空对象
self.__init__(obj, "1.1.1.1", 9999) # 为类赋予其独有属性
self._instance = obj # 添加一个_instance属性
def __call__(self, *args, **kwargs):
# print("_instance--> ", self._instance.__dict__)
if len(args) == 0 and len(kwargs) == 0:
return self._instance
obj = self.__new__(self)
self.__init__(obj, *args, **kwargs)
return obj
class Mysql(object, metaclass=Mymeta):
def __init__(self, host, port):
self.host = host
self.port = port
m1 = Mysql()
m2 = Mysql()
print(m1)
print(m2)
x = Mysql("1.1.1.1", 3333)
print(x)
# 说明
# 1、元类 __init__ 方法,在类Mysql创建之始执行, 先将Mysql类名,基类, 代码丢到名称空间中
# 2、__init__ 在创建之始时,先生成了一个空对象,并赋值了一个独有属性 _instance
# 3、当类被调用时会执行__call__方法, 此时会判断args跟kwargs 参数是否为空,如果是空会直接返回 在创建之始时__init__生成的_instance属性
-
使用?
当实例化多次得到的对象中存放的属性都一样的情况,应该将多个对象指向同一个内存地址
-
实现方式一: 类当中定义类方法
import settings class Oracle: __isinstance=None # 实例化 def __init__(self, ip, port): self.ip = ip self.port = port @classmethod # 由类来调用 def set_conf(cls): # 如果Oracle.__isinstance为空, 那么就初始化配置 if cls.__isinstance is None: cls.__isinstance = cls(settings.IP, settings.PORT) return cls.__isinstance o=Oracle.set_conf() print(o.ip) # 自动传参 print(id(o)) # 此时在打印该对象,内存地址都是一样的 print(id(o))
-
实现方式二: 使用装饰器的方式
import settings def single(obj): __isinstance = obj(settings.IP, settings.PORT) def wrapper(*args, **kwargs): # 如果用户没有传递参数,说明要使用默认的 isinstance if len(args) == 0 and len(kwargs) == 0: return __isinstance return obj(*args, **kwargs) return wrapper @single class Oracle: def __init__(self, ip, port): self.ip = ip self.port = port # 单例模式 o1 = Oracle() # <__main__.Oracle object at 0x00000000022214A8> o2 = Oracle() # <__main__.Oracle object at 0x00000000022214A8> print(o1) # 也可以自己传递参数,就不是一个单例模式了 o3 = Oracle("1.1.1.1",3309) # <__main__.Oracle object at 0x00000000022017B8>
-