python面向对象编程进阶(7)

2018-04-24  本文已影响10人  纳米君
  1. 对类的属性可以做出一些限制,需声明__slots__属性。
    一般情况下,只限制类自身属性。当存在继承关系时,分情况如下:
    子父类都有该属性,那么父类受自身影响,子类继承父类的限制,双重影响;
    父类有该属性,子类没有,那么父类受自身影响,子类不受影响;
    子类有该属性,父类没有,那么父类和子类都不受影响。

  2. 针对私有变量,简化getter、setter方法。

  3. 对一些方法(比如__str____repr__ 等等)的用途作出简单介绍。

直接看例子和注释:

class Person:
    # 限制Person类只能有name、age属性,注意是tuple类型
    __slots__ = ('name', 'age')

    # print(p)调用该方法,如果不自定义__str__方法,会输出<__main__.Person object at 0x00000000028DBB38>
    # 自定义之后,就可以输出指定格式了。
    def __str__(self):
        return 'Person object (name: %s)' % self.name

    # 命令行执行p后,会调用该方法
    __repr__ = __str__

    def do(self):
        print('person...')


class Student(Person):  
    __slots__ = ('sex',)


p = Person()
p.name = 'tom'
print(p.name)
print(p)

s = Student()
s.age = 23
print(s.age)


class Teacher:

    def __init__(self, name):
        self._name = name

    # 简化getter方法,针对私有变量
    @property
    def name(self):
        # _name不能和函数名同名,即不能写成self.name
        return self._name

    # 简化setter方法,声明@property之后,就会有@函数名.setter装饰器
    @name.setter
    def name(self, name):
        if isinstance(name, str):
            self._name = name
        else:
            raise ValueError('str is required')


t = Teacher('fuck')
print(t.name)
t.name = 'tom'
print(t.name)

继承时,主线一般都是单一继承下来。当需要扩展功能时,只需设计额外的功能类,多重继承即可。这种设计一般称为MixIn。比如:

#会跑
class RunnableMixIn:
    def do(self):
        print('run...')

#会飞
class FlyMixIn:
    def do(self):
        print('fly...')

#多重继承,当父类中至少有两个父类拥有同样的方法时,子类调用该方法时遵循拓扑排序。关于拓扑排序会在下一篇当中专门介绍。
class My(Person, FlyMixIn, RunnableMixIn):
    pass


m = My()
m.do()

如果想把对象定义成像list一样操作数据,需定义下面几个方法:

# 斐波那契数列
class Fib:

  def __init__(self):
     self.a = 0
     self.b = 1

  # 当需要迭代对象时,需要定义__iter__和__next__方法
  def __iter__(self):
      return self

  def __next__(self):
      self.a, self.b = self.b, self.a + self.b
      return self.a

  # 当需要和list一样用下标取值时,需要定义__getitem__方法
  # item可能是下标,也可能是slice对象,当需要和list一样使用切片时,需做一下判断
  # 如果把对象看成dict,item就看成是key
  def __getitem__(self, item):
     if isinstance(item, int):
         a, b = 1, 1
         for i in range(item):
             a, b = b, a + b
         return a
     elif isinstance(item, slice):
         # start stop可能为负数,这里暂时只考虑正数
        start = item.start
        if start is None:
          start = 0
        stop = item.stop
        value = []
        a, b = 1, 1
        for i in range(stop):
            if i >= start:
              value.append(a)
            a, b = b, a + b
        # 每几个取一个
        step = item.step
        new_value = []
        if step is not None and step != 1:
            for i in range(len(value)):
                if i * step < len(value) - 1:
                    new_value.append(value[i * step])
            return new_value
        else:
            return value

  # 给对象进行赋值的方法
  def __setitem__(self, key, value):
      pass

  # 删除对象某个元素的方法
  def __delitem__(self, key):
      pass

  # 当我们get对象中不存在的属性时,会报错,自定义__getattr__方法可以对没定义的属性进行赋值
  # 如果该属性已经定义了,该方法针对该属性的赋值就无效
  def __getattr__(self, item):
      if item == 'c':
          return 100

for v in Fib():
  if v < 100:
      print(v)
  else:
      break

print(Fib()[2])
print(Fib()[:10])
print(Fib()[:10:2])
print(Fib().c)

链式调用,定义__getattr__()和__call__()方法,利于组装url

class Chain:

      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__

      # 对象实例()会调用该方法
      def __call__(self, args):
          return Chain('%s/%s' % (self._path, args))


print(Chain().status.user.timeline.list) --> 结果:/status/user/timeline/list
print(Chain().users('michael').repos) --> 结果:/users/michael/repos
# callable(obj) 判断obj是否是callable,class中必须要有__call__方法,部分函数例外
print(callable(Chain())) --> 结果:True
print(callable([1, 2])) --> 结果:False
print(callable(max)) --> 结果:True

type()函数可以查看变量或者函数的类型。

# 类型是class type
print(type(Chain))
# 类型是class Chain
print(type(Chain()))

type(args1, args2, args3)函数还可以可以创建类。

args1: 类名
args2: 父类tuple
args3: 类中方法名绑定方法
一般采用class Xxx声明类,Python解释器遇到class定义时,只是扫描一下class定义的语法,然后调用type()方法创建class,所以动态语言Python本身支持运行期动态创建class。

def fn(self, name='233'):
    print('name: %s' % name)

# 创建类
Test = type('Test', (object,), dict(test=fn))
# name: HelloTest
Test().test('HelloTest')
上一篇下一篇

猜你喜欢

热点阅读