python-面向对象-2-高级

一、类型判断

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基类提供的静态方法,其主要作用有两个

  1. 先造出一个类的空对象 如 obj=self.__new__(self)
  2. 为该空对象初始化独有属性 self.__init__(obj, *args, **kwargs)
  3. 返回 该 对象 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属性
  1. 使用?

    当实例化多次得到的对象中存放的属性都一样的情况,应该将多个对象指向同一个内存地址

    1. 实现方式一: 类当中定义类方法

      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))
      
    2. 实现方式二: 使用装饰器的方式

      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>
      

猜你喜欢

转载自blog.csdn.net/u010304195/article/details/113108553