python基础知识(3)

目录

面向对象高级编程

__slots__

@property

多重继承

定制类(__str__、__iter__...)

枚举类

object、type、metaclass

object

type类

元类metaclass


面向对象高级编程

  • __slots__

    • 限制实例的属性,__slots__ 更多的是用来作为一个内存优化工具
    • 当实例化几万个对象的时候,每个对象都会生成一个名称空间dict,而每一个名称空间都会各自占用一个内存,造成内存的浪费,用__slots__时候就不再产生dict了,从而省空间
    • class Student(object):
          __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
    • __slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的 
  • @property

    • 我们可以给类添加set和get方法,访问__xx的变量,用于参数校验,@property更简便
class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value


s.score = 60   #设置
s.score        #获取

"""
不定义setter方法就是一个只读属性
"""
  • 多重继承

    • 通过多重继承,一个子类可以同时获得多个父类的所有功能。
    • MixIn。需要“混入”额外的功能,设计通常称之为MixIn
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
    pass
  • 定制类(__str__、__iter__...)

    • __str__。print打印对象输出的内容,不用print打印,调用的是__repr__
      • __str__给用户看,__repr__用于调试服务的
    • __iter__。类变成类似可迭代对象,执行iter()会调用此方法,返回一个迭代器
    • __next__。查看这里就明白了:迭代器
    • class Fib(object):
          def __init__(self):
              self.a, self.b = 0, 1 # 初始化两个计数器a,b
      
          def __iter__(self):
              return self # 实例本身就是迭代对象,故返回自己
      
          def __next__(self):
              self.a, self.b = self.b, self.a + self.b # 计算下一个值
              if self.a > 100000: # 退出循环的条件
                  raise StopIteration()
              return self.a # 返回下一个值
      
      
      """
      __iter__()方法,该方法返回一个迭代对象,
      
      Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值
      
      直到遇到StopIteration错误时退出循环
      """
    •  __getitem__。a[b],索引方式获取值时候调用
    • class Fib(object):
          def __getitem__(self, n):
              if isinstance(n, int): # n是索引
                  a, b = 1, 1
                  for x in range(n):
                      a, b = b, a + b
                  return a
              if isinstance(n, slice): # n是切片
                  start = n.start
                  stop = n.stop
                  if start is None:
                      start = 0
                  a, b = 1, 1
                  L = []
                  for x in range(stop):
                      if x >= start:
                          L.append(a)
                      a, b = b, a + b
                  return L
      
      f = Fib()
      f[5]      #索引
      f[0:5]    #切片
    • __setitem__。 a[b] = c,索引方式设置值时候调用
    • __ delitem__。del a[b],索引方式删除时候调用
    • __getattr__。a.b,点方式获取时候调用
      • 方式会先获取实例属性,没有的话找类属性,再不存在就去调用__getattr__方法
      • 私有属性__xx就相当于不存在(其他属性没影响),会直接调用此方法
      • getattr()。这方法找属性也是上面这个步骤,getattr(obj,'attr',default)
        • 只是找不到时候返回一个默认值
        • 而且他的attr是一个字符串形式
    • __getattribute__。任何的点方式访问任何的属性都会调用此方法(除了类调用类属性)
      • 此方法先于__getattr__方法调用。顺序是这样的,先访问__getattribute__,如果发现属性不存在,抛出AttributeError异常,如果这时候我们实现了__getattr__方法,就会调用__getattr__方法,不会报错,如果没实现就抛出AttributeError异常。
      • """
        但是要防止递归调用
        """
        class man(object):
            def __getattribute__(self, attr):
                return self.__dict__[attr]
        
        # 这种方式肯定是递归调用了,self.__dict__[attr],又是访问自己了
        
        """
        应该去访问基类或者超类的方法
        """
        class man(object):
            def __getattribute__(self, attr):
                return super().__getattribute__(attr)
    • __setattr__。a.b =c,点方式设置时候调用
      • 设置特殊属性(如__dict__)是不会调用此方法的
        • """
          所以我们才可以这样
          """
          class test_attr(object):
          
              def __setattr__(self,key,value):
                  self.__dict__[key] = value
      • setattr()。这个方法和a.b方法效果一样,但是,获取属性时候输入的是字符串形式,这时候你可以动态生成属性了,非常有用
    • __delattr__。del a.b,点方式删除时候调用
    • __call__。对象可被调用
    • __del__。del obj。每当 del 实例化对象时会触发
      • 此方法一般无须定义,一般无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
    • __enter__、__exit__。上下文管理器
    • __doc__。类注释信息就放在这个特殊变量里面,如下的“jgh”
      • class test(object):
            """jgh"""
            pass
  • 枚举类(比写类变量好用)

from enum import Enum
class Color(Enum):
    red = 1
    orange = 2
    yellow = 3
    green = 4
    blue = 5
    indigo = 6
    purple = 7

"""
1、枚举取值
1)通过成员的名称来获取成员
Color['red']
2)通过成员值来获取成员
Color(2)
3)通过成员,来获取它的名称和值
red_member = Color.red
red_member.name
red_member.value

2、可迭代
for color in Color:
    print(color)

如果枚举有值重复的成员,循环遍历枚举时只获取值重复成员的第一个成员
如果想把值重复的成员也遍历出来,要用枚举的一个特殊属性__members__

for color in Color.__members__.items():
    print(color)

3、枚举比较
Color.red is Color.red
olor.blue == Color.red



1.1 定义枚举时,成员名称不允许重复
1.2 默认情况下,不同的成员值允许相同。但是两个相同值的成员,第二个成员的名称被视作第一个成员的别名
1.3 如果枚举中存在相同值的成员,在通过值获取枚举成员时,只能获取到第一个成员
1.4 如果要限制定义枚举时,不能定义相同值的成员。可以使用装饰器@unique

"""
  • object、type、metaclass

    • object

    • type类

      • 一切皆对象,一切对象皆由type创建
      • 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
      • class的定义是运行时动态创建的,而创建class的方法就是使用type()函数
        • 当我们定义一个class
        • class MyClass:
              data = 1
        • python真正执行的是下面这段代码,就是 type 的 __call__ 运算符  
        • class = type(classname, superclasses, attributedict)
        • 然后进一步调用
        • type.__new__(typeclass, classname, superclasses, attributedict)
          type.__init__(class, classname, superclasses, attributedict)
        • def fn(self, name='world'): # 先定义函数
              print('Hello, %s.' % name)
          
          Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello 类
          
          print(type(Hello)) # <class 'type'>
          
          """
          要创建一个class对象,type()函数依次传入3个参数:
          
          1、class的名称;
          2、继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
          3、class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上
          
          正常的 Hello 定义,和你手工去调用 type 运算符的结果是完全一样的
          """
    • 元类metaclass

      • metaclass 是 type 的子类,通过替换 type 的 __call__ 运算符重载机制,“超越变形”正常的类
      • 我们来看看metaclass的执行过程就能一目了然了
      • class Mymeta(type):
             def __init__(self, name, bases, dic):
                 super().__init__(name, bases, dic)
                 print('===>Mymeta.__init__')
                 print(self.__name__)
                 print(dic)
                 print(self.yaml_tag)
         
             def __new__(cls, *args, **kwargs):
                 print('===>Mymeta.__new__')
                 print(cls.__name__)
                 return type.__new__(cls, *args, **kwargs)
         
             def __call__(cls, *args, **kwargs):
                 print('===>Mymeta.__call__')
                 obj = cls.__new__(cls)
                 cls.__init__(cls, *args, **kwargs)
                 return obj
         
        class Foo(metaclass=Mymeta):
             yaml_tag = '!Foo'
         
             def __init__(self, name):
                 print('Foo.__init__')
                 self.name = name
         
             def __new__(cls, *args, **kwargs):
                 print('Foo.__new__')
                 return object.__new__(cls)
        
        """
        输出:
        ===>Mymeta.__new__
        Mymeta
        ===>Mymeta.__init__
        Foo
        
        {'__module__': '__main__', '__qualname__': 'Foo', 'yaml_tag': '!Foo', '__init__': <function Foo.__init__ at 0x0000000007EF3828>, '__new__': <function Foo.__new__ at 0x0000000007EF3558>}
        !Foo
        """
        
        foo = Foo('foo')
        """
        输出
        ===>Mymeta.__call__
        Foo.__new__
        Foo.__init__
        """
      • 从上面的运行结果可以发现在定义 class Foo() 定义时,会依次调用 MyMeta 的 __new__ 和 __init__ 方法构建 Foo 类,其中__new__就会返回type的实例,也就是我们定义的类Foo,然后在调用 foo = Foo() 创建类的实例对象时,才会调用 MyMeta 的 __call__ 方法,__call__方法里面调用 Foo 类的 __new__ 和 __init__ 方法    
      • """
        metaclass可以给我们自定义的MyList增加一个add方法
        
        metaclass是类的模板,所以必须从`type`类型派生
        """
        
        
        class ListMetaclass(type):
            def __new__(cls, name, bases, attrs):
                attrs['add'] = lambda self, value: self.append(value)
                return type.__new__(cls, name, bases, attrs)
        
        class MyList(list, metaclass=ListMetaclass):
            pass
        
        
        """
        当我们传入关键字参数metaclass时
        
        __new__()方法接收到的参数依次是和type是一样的:
        
        1、当前准备创建的类的对象;
        2、类的名字;
        3、类继承的父类集合;
        4、类的方法集合。
        """
      •  通过metaclass修改类定义的意义。ORM就是一个典型的例子。
        • ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表
        • """
          利用meteclass动态修改类来实现ORM
          
          以下是我们期待的写法
          """
          class User(Model):
              # 定义类的属性到列的映射:
              id = IntegerField('id')
              name = StringField('username')
              email = StringField('email')
              password = StringField('password')
          
          # 创建一个实例:
          u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
          # 保存到数据库:
          u.save()
          
          
          
          """
          首先来定义Field类,它负责保存数据库表的字段名和字段类型
          """
          
          class Field(object):
          
              def __init__(self, name, column_type):
                  self.name = name
                  self.column_type = column_type
          
              def __str__(self):
                  return '<%s:%s>' % (self.__class__.__name__, self.name)
          
          
          """
          在Field的基础上,进一步定义各种类型的Field,比如StringField,IntegerField
          """
          
          class StringField(Field):
          
              def __init__(self, name):
                  super(StringField, self).__init__(name, 'varchar(100)')
          
          class IntegerField(Field):
          
              def __init__(self, name):
                  super(IntegerField, self).__init__(name, 'bigint')
          
          """
          定义ModelMetaclass
          """
          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)
                  
                  # 将Field类型的属性保存在mapping中
                  mapping  = dict()
                  for k,v in attrs.items():
                      if isinstance(v,Field):
                          print('Add Field %s to mapping' % v.name)
                          mapping[k] = v
                          
                  # 从类属性中删除该Field属性
                  for k in mapping.keys():
                      attrs.pop(k)
                  attrs['__mapping__'] = mapping  #保存属性和列的映射关系
                  attrs['__table__'] = name
                  return type.__new__(cls,name,bases,attrs)
          
          
          """
          定义Model类,修改dict的类创建方式,基于Modelclass
          """
          
          class Model(dict,metaclass=ModelMetaclass):
              
              def __init__(self,**kw):
                  super().__init__(**kw)
                  
              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
                  
              #定义save方法保存到数据库
              def save(self):
                  fields = list()
                  args = list()
                  for k, v in self.__mapping__.items():
                      fields.append(v.name)
                      args.append(getattr(self, k, None))  #根据定义的类属性名,查找值,保存在args中 
                  #sql = 'insert into %s (%d) values (%s)' % (self.__table__, ','.join(fields), ','.join(args))
                  print('SQL: %s' % fields)
                  print('ARGS: %s' % str(args))
          
          
          """
          getattr(self,k,None)的效果是:先获取实例的属性;若无,则获取类的属性;若无,则调用__getattr__方法。
          
          对u调用getattr方法,因为u没有实例属性,而类属性已经通过attrs.pop(k)删掉,
          
          因此会调用__getattr__,该方法返回u的键值
          """

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值