python—面向对象高级编程

栏目: 后端 · 前端 · 发布时间: 5年前

内容简介:最终的赋值的终点是

面向对象高级编程

使用slots

  • 动态语言可以随时修改和添加类与对象的属性和方法.python自然也不例外.
  • 过于自由的修改,对类和对象本身不利,python中使用 __slots__ 来约束这种行为.

    class Student(object):
      __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
    
  • 限制类中只能再增加 name 和 age属性.仅对 Student 类生效,对子类无效.

使用@property

  • 装饰器,不仅在函数中其作用,还可以在类中使用,限定属性的读写和范围.
  • 类属性的读写,都是通过get/set方式进行,太过繁琐.Python内置的 @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
    

最终的赋值的终点是 self._score .操作时候直接访问 xxx.score 即可.

  • 限定只读,只定义getter,不定义setter.

    class Student(object):
    
      @property
      def birth(self):
          return self._birth
    
      @birth.setter
      def birth(self, value):
          self._birth = value
    
      @property
      def age(self):
          return 2015 - self._birth
    

    birth 可写,age 只读.

多重继承

  • 没什么多说的.

    class Bat(Mammal, Flyable):
      pass
    

    多重继承的设计,相对 java 的单继承,自由度更大,但能不能用好,取决于写代码的人.

定制类

  • 类中形如 __xxx__ 的变量或者函数名

str&&repr

  • __str____repr__ 是类/对象的打印输出. str 是调用print 函数的输出, repr 是 直接输出,常用于调试.

  • 示例:

    class Student(object):
      def __init__(self, name):
          self.name = name
      def __str__(self):
          return 'Student object (name=%s)' % self.name
      __repr__ = __str__
    

iter&&next

  • 用于 for 循环, __iter__ 用于返回一个可迭代对象(通常是自身). 同时 for 循环时,不断调用 __next__ ,获取下一个结果,直到遇到 StopIteration 错误.
  • 示例:

    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 # 返回下一个值
    

getitem

  • 通过 __getitem__ 可以将一个对象,包装成一个list dict tuple.
  • 示例: list (包含不完整切片)

    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
    

    对于切片的负数 和 [:5] 没有处理.

  • dict时,相对应的还有 __setitem__() __delitem__() .
  • 主要是python是非强类型的语言,不必非要继承并实现接口.

getattr

  • 当试图调用不存在的属性和方法时,报错.此时需要找 __getattr__ .
  • __getattr__ 中直接返回属性和函数均可.当未匹配时返回 None.也可以自定义返回的错误类型.
  • 示例

    class Student(object):
    
      def __getattr__(self, attr):
          if attr=='age':
              return lambda: 25
          raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
    
  • 可以把一个类的所有属性和方法调用全部动态化处理(理解暂时不太清除,仅记录)

  • 现在很多网站都搞REST API,比如新浪微博、豆瓣啥的,调用API的URL类似:

  • 如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。

  • 利用完全动态的 getattr ,我们可以写出一个链式调用

    class Chain(object):
    
      def __init__(self, path=''):
          self._path = path
    
      def __getattr__(self, path):
          return Chain('%s/%s' % (self._path, path))
    
      def __str__(self):
          return self._path
    
      __repr__ = __str__
    
    >>> Chain().status.user.timeline.list
    '/status/user/timeline/list'
    
  • 无论API怎么变,SDK都可以根据URL实现完全动态的调用,而且,不随API的增加而改变!

call

  • python中函数与对象的区别十分模糊,所谓函数实际上可以看作是实现了 __call__ 的一类对象,反过来一个对象通过添加(动态/静态) __call__ 也可以当作函数调用.
  • 判断一个对象是否可当作函数调用,使用 callable 函数判断,可调用,返回True.
  • 示例:

    class Student(object):
      def __init__(self, name):
          self.name = name
    
      def __call__(self):
          print('My name is %s.' % self.name)
    
    >>> s = Student('Michael')
    >>> s() # self参数不要传入
    My name is Michael.
    

枚举类

  • 枚举用于限定变量的值域,防止错误的赋值导致的严重后果.Python提供了Enum类.
  • 简单定义

    from enum import Enum
    
    Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
    
  • 枚举类定义

    from enum import Enum, unique
    
    @unique # 防止值重复
    class Weekday(Enum):
        Sun = 0 # Sun的value被设定为0
        Mon = 1
        Tue = 2
        Wed = 3
        Thu = 4
        Fri = 5
        Sat = 6
    

元类

  • python作为动态语言,类和函数不是在编译时定义,而是运行时动态创建,除了编写代码创建类/函数外,python亦可以通过 type/metaclass 动态创建类/函数.

  • 实际上,即使在python 中直接定义的类/函数 也是在运行时通过 type() 载入的,因此也可以直接通过 type() 直接创建.

  • 示例:

    >>> def fn(self, name='world'): # 先定义函数
    ...     print('Hello, %s.' % name)
    ...
    >>> Hello = type('Hello', (object,), dict(hello=fn)) #   创建Hello class, 类名,  父类       ,方法
    >>> h = Hello()
    >>> h.hello()
    Hello, world.
    >>> print(type(Hello))
    <class 'type'>
    >>> print(type(h))
    <class '__main__.Hello'>
    
  • metaclass 元类 ,简单来说,对象来源于类,而python中的 类 可以来源于 metaclass 元类. 通过 metaclass 可以非常简单的实现 对类的生成和修改.

  • 示例:习惯上 metaclass 的类名总是以 Metaclass 结尾

    # metaclass是类的模板,所以必须从`type`类型派生:
    class ListMetaclass(type):
      #   创建类的对象,类的名称,父类  , 方法集合
      def __new__(cls, name, bases, attrs):
          #方法添加 add 方法
          attrs['add'] = lambda self, value: self.append(value)
          return type.__new__(cls, name, bases, attrs)
    
    class MyList(list, metaclass=ListMetaclass):
      pass
    

    创建 MyList 的时候,调用了 ListMetaclass.__new__() , 在创建时添加了 add 方法.

  • 示例2:ORM数据库(简)

    一个ORM数据库,需要动态的根据字段,生成类,与java中不同,python 非常简单.

    • 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

      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):
          if name=='Model':#如果是 Model 基类,不修改属性等.
              return type.__new__(cls, name, bases, attrs)
          print('Found model: %s' % name)
          mappings = dict()
          for k, v in attrs.items():#保存 子类所有属性到mappings
              if isinstance(v, Field):
                  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)
      
    • 基类Model

      class Model(dict, metaclass=ModelMetaclass):
      
        def __init__(self, **kw):#接收元组
            super(Model, self).__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
        
        def save(self):
            fields = []
            params = []
            args = []
            for k, v in self.__mappings__.items():
                fields.append(v.name)
                params.append('?')
                args.append(getattr(self, k, None))
            sql = 'insert into %s (%s) values (%s)' %    (self.__table__, ','.join(fields), ','.join   (params))
            print('SQL: %s' % sql)
            print('ARGS: %s' % str(args))
      
    • 使用

      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()
      
    • ListMetaclass.__new__()
      

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

商业的常识

商业的常识

申音 / 山西经济出版社 / 2011-7-1 / 35.00元

★为什么美国没有史玉柱,中国没有乔布斯? ★什么是“对的行业”、“错的行业”? ★我们需要什么样的营销? ★老板为什么要读商学院? ★山寨公司还需要管理吗? ★资源问题是个“伪问题”? ★别把商业模式当成葵花宝典 ★给海归技术创业兄弟的九个忠告 ★在一个不伟大的行业里,做一个伟大的公司 ★是什么让互联网遭遇了有史以来最鸡犬不宁的一战?一起来看看 《商业的常识》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具