1,面向对象编程思想

类:一类具有相同属性的抽象

属性(静态属性):实例变量、类变量、私有属性

方法(动态属性):构造函数、析构函数(默认就有)、函数、私有函数

对象/实例:类经过实例化后,就是对象/实例

封装 encapsulation:隐藏对象的属性和实现细节,仅对外公开接口

继承 inheritance:类派生子类

多态 polymorphism:一个接口,多种实现

举例:

  1. class Foo: # 类
  2. t = 'd' # 类变量,所有实例共享
  3. def __init__(self, x, y): # 构造函数
  4. self.x = x # 属性,实例变量
  5. self._y = y # 私有属性,实例变量
  6.  
  7. def _test(self): # 私有方法
  8. return self.x
  9.  
  10. def __del__(self): # 析构函数
  11. pass
  12.  
  13. def run(self):
  14. print(self.x, self._y)
  15.  
  16. f = Foo(1, 2) # 类的实例化
  17. f.run() # 调用实例f的方法run
  18. Foo.run(f) # 调用实例f的方法run

类变量t,可以通过Foo.t,  type(self).t,  self.__class__.t, @classmethod的cls.t去访问和修改。

如果用self.t虽然也可以访问,但修改类变量时,如果t是不可变参数会创建副本。如果是可变序列则用self.t访问和用类访问一样会就地修改,参考函数的传参。

  1. >>>f.t
  2. d
  3. >>>f.t = 'e'
  4. >>>f.t.
  5. e
  6. >>>Foo.t
  7. d

2,继承

代码重用

基类不应该被单独实例化

2.1,继承的方法

  1. class Human:
  2. pass
  3.  
  4. class Male(Human):
  5. pass

2.2,子类继承父类的构造函数或方法

不会自动获得父类构造函数或方法,直接写会覆盖父类的构造函数或者同名方法。

如果想要获得父类的构造函数或者方法,需要通过继承获得。

可以通过super()或指定父类继承构造函数或者方法:

  1. class Base:
  2. def __init__(self):
  3. print('BASE_init')
  4.  
  5. def say(self):
  6. print('BASE_say')
  7.  
  8. class A(Base):
  9. def __init__(self):
  10. super().__init__() # 通过super()继承构造函数
  11.  
  12. def say(self):
  13. super().__init__() # 通过supe r()继承方法
  14.  
  15. class B(Base):
  16. def __init__(self):
  17. Base.__init__(self) # 通过指定父类继承构造函数
  18.  
  19. def say(self):
  20. Base.__init__(self) # 通过指定父类继承方法

  

2.3,指定父类多重继承

指定父类多重继承构造函数或者方法,结果相同,本节以构造函数为例。

指定父类多重继承:

  1. class Base:
  2. def __init__(self):
  3. print('BASE')
  4.  
  5. class A(Base):
  6. def __init__(self):
  7. Base.__init__(self)
  8. print('A')
  9.  
  10. class B(Base):
  11. def __init__(self):
  12. Base.__init__(self)
  13. print('B')
  14.  
  15. class C(A, B):
  16. def __init__(self):
  17. A.__init__(self)
  18. B.__init__(self)
  19. print('C')

上述代码中,AB都指定继承Base,C先后指定继承AB。

对C实例化时,先后执行AB的构造函数,再执行C自己的构造函数,但Base的构造函数被执行了2遍。

  1. >>> c = C()
  2. BASE
  3. A
  4. BASE
  5. B
  6. C  

2.4,super()多重继承

super()多重继承构造函数或者方法,结果相同,本节以构造函数为例。

每个重新定义过的覆盖方法都使用了super()方法时,继承时会遍历整个MRO列表,并且让每个方法只调用一次。(全有super:遍历)

不符合上面条件时,继承时会按照MRO列表继承,继承时执行广度优先继承,同时不会遍历MRO列表,如果某个类有super()时,会继承该类后面的类的方法。(非全有super:广度优先 + MRO列表super)

2.5实验中,C的MRO列表打印如下:

  1. >>> C.__mro__
  2. (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)

1)super()多重继承 — A和B都使用了super()

  1. class Base:
  2. def __init__(self):
  3. print('BASE')
  4.  
  5. class A(Base):
  6. def __init__(self):
  7. super().__init__()
  8. print('A')
  9.  
  10. class B(Base):
  11. def __init__(self):
  12. super().__init__()
  13. print('B')
  14.  
  15. class C(A, B):
  16. def __init__(self):
  17. super().__init__()
  18. print('C')

C实例化:

  1. >>> c = C()
  2. BASE
  3. B
  4. A
  5. C

Base,B,A,C都得到了遍历执行,并且Base的构造方法只执行了一遍。

2)super()多重继承 — A和B都没有使用super()

  1. class Base:
  2. def __init__(self):
  3. print('BASE')
  4.  
  5. class A(Base):
  6. def __init__(self):
  7. print('A')
  8.  
  9. class B(Base):
  10. def __init__(self):
  11. print('B')
  12.  
  13. class C(A, B):
  14. def __init__(self):
  15. super().__init__()
  16. print('C')

C实例化:

  1. >>> c = C()
  2. A
  3. C

根据MRO列表优先只继承A。

如果A没有构造方法,根据广度优先继承B,输出结果是B,C。

3)super()多重继承 — B有super()

C实例化时,输出结果为AC。同2)super()多重继承 — A和B都没有使用super()

如果A没有构造方法,根据广度优先继承B,输出结果是BASE,B,C。(多个BASE是因为B有super())

4)super()多重继承 — A有super()

  1. class Base:
  2. def __init__(self):
  3. print('BASE')
  4.  
  5. class A(Base):
  6. def __init__(self):
  7. super().__init__()
  8. print('A')
  9.  
  10. class B(Base):
  11. def __init__(self):
  12. print('B')
  13.  
  14. class C(A, B):
  15. def __init__(self):
  16. super().__init__()
  17. print('C')

奇怪的现象出现了:

  1. >>> c = C()
  2. B
  3. A
  4. C

这是因为根据MRO列表,顺序为C/A/B/BASE,A的super()继承的是B的构造函数,并不是BASE的构造函数。

 

2.5,使用mixin类技术扩展类的功能

mixin类时为了扩展其他类的功能而创建的,不能直接实例化,没有__init__(),也没有实例变量

mixin生效的关键是通过super()把任务转交给MRO中的下一个类。

MRO解析顺序为从左至右(object),所以需要注意继承的次序,不确定的话可以通过print(cls.mro())查看。

mixin类实例一:记录字典赋值过程

  1. class LoggedMappingMixin:
  2.  
  3. __slots__ = ()
  4.  
  5. def __getitem__(self, item):
  6. print('Getting ' + str(item))
  7. return super().__getitem__(item)
  8.  
  9. def __setitem__(self, key, value):
  10. print('Setting {} = {}'.format(key, value))
  11. return super().__setitem__(key, value)
  12.  
  13. def __delitem__(self, key):
  14. print('Deleting ' + str(key))
  15. return super().__delitem__(key)

使用mixin类:

  1. class LoggedDict(LoggedMappingMixin, dict): # 需要先继承mixin类
  2. pass
  3.  
  4. >>>LoggedDict.mro() # mixin的super继承的是dict
  5. [<class '__main__.LoggedDict'>, <class '__main__.LoggedMappingMixin'>, <class 'dict'>, <class 'object'>]
  6. >>>d['x'] = 42
  7. Setting x = 42
  8. >>>d['x']
  9. Getting x
  10. 42

mixin类实例二:实现key类型限制

  1. class RestrictKeysMixin:
  2. def __init__(self, *args, _restrict_key_type, **kwargs):
  3. self._restrict_key_type = _restrict_key_type
  4. super().__init__(*args, **kwargs)
  5.  
  6. def __setitem__(self, key, value):
  7. if not isinstance(key, self._restrict_key_type):
  8. raise TypeError('Keys must be ' + str(self._restrict_key_type))
  9. super().__setitem__(key, value)
  10.  
  11. class Rdict(RestrictKeysMixin, dict):
  12. pass
  13.  
  14. d = Rdict(_restrict_key_type=str)
  15. d[42] = 10 # TypeError: Keys must be <class 'str'>

3,多态

3.1,实现多态

在父类中的一个方法,输入不同子类的对象时,可以实现不同的效果。

父类:

  1. class Human:
  2.  
  3. def __init__(self, name, age):
  4. self.name = name
  5. self.age = age

子类Male:

  1. class Male(Human):
  2.  
  3. def hobby(self):
  4. print('sports')

子类Female:

  1. class Female(Human):
  2.  
  3. def hobby(self):
  4. print('shopping')

不同子类,hobby输出不同,实现了多态:

  1. m = Male('m', 20)
  2. f = Female('f', 20)
  3. m.hobby() # sports
  4. f.hobby() # shopping

还可以编写一个入口函数,统一输出不同对象的hobby:

  1. def get_hobby(obj):
  2. obj.hobby()
  3.  
  4. get_hobby(m) # sports
  5. get_hobby(f) # shopping

3.2,通过抽象基类强制要求子类必须实现方法

如果想要求子类在继承时必须实现hobby方法,可以对父类进行如下改造:

  1. import abc
  2.  
  3. class Human(abc.ABC):
  4.  
  5. def __init__(self, name, age):
  6. self.name = name
  7. self.age = age
  8.  
  9. @abc.abstractmethod
  10. def hobby(self): # 抽象方法无需实现,直接注释或pass即可
  11. """必须实现hobby"""

4,内置三大类装饰器

4.1,@staticmethod

如果一个方法在类中定义,但没有用到类中的实例属性或者方法,建议加上staticmethod或者classmethod。

staticmethod:本质上就是放在类中的全局函数(可以通过类或实例调用),和类没关系,完全可以放在类的外面。

  1. class Foo:
  2. def __init__(self, x):
  3. self.x = x
  4.  
  5. @staticmethod
  6. def run():
  7. print('hello world')
  8.  
  9. f = Foo(1)
  10. f.run() # 输出hello world,通过类的实例调用
  11. Foo.run() # 输出hello world,通过类调用

 4.2,@classmethod

被@classmethod装饰的函数可以通过类或者类的实例调用。

和staticmethod的区别是可以调用类的属性或方法:

  1. class Foo:
  2. hw = 'hello world'
  3.  
  4. @classmethod
  5. def klass_run(cls):
  6. print(cls.hw)
  7.  
  8. f = Foo()
  9. f.klass_run() # 输出hello world,通过类的实例调用
  10. Foo.klass_run() # 输出hello world,通过类调用

被classmethod装饰的函数第一个参数是类的引用,相当于类的实例self执行type(self),或者self.__class__,或者直接调用类Foo。下面实现了相同的效果:

  1. class Foo:
  2. hw = 'hello world'
  3.  
  4. def run(self): # 下面三个print实现了相同的效果
  5. print(type(self), type(self).hw)
  6. print(self.__class__, self.__class__.hw)
  7. print(Foo, Foo.hw)
  8.  
  9. @classmethod
  10. def klass_run(cls):
  11. print(cls, cls.hw)
  12.  
  13. >>>f = Foo()
  14. >>>f.run()
  15. <class '__main__.Foo'> hello world
  16. <class '__main__.Foo'> hello world
  17. <class '__main__.Foo'> hello world
  18. >>>Foo.klass_run()
  19. <class '__main__.Foo'> hello world
  20. >>>f.klass_run()
  21. <class '__main__.Foo'> hello world

  

4.3,@property

主要三个功能,详见笔记‘类的属性’:

  • 函数变属性
  • 属性只读
  • 属性管理

5,反射

有时建立完类以后,需要动态操纵属性或方法。

可以对类,或者类的实例进行操作

  1. class Foo:
  2. def __init__(self, x):
  3. self.x = x
  4.  
  5. def run(self):
  6. print('hello world')
  7.  
  8. f = Foo(1)

hasattr:测试类/实例是否有属性/方法

  1. print(hasattr(Foo, 'x')) # False
  2. print(hasattr(f, 'x')) # True
  3. print(hasattr(Foo, 'run')) # True
  4. print(hasattr(f, 'run')) # True

getattr:获取类/实例的属性/方法,如果某个方法保存在字符串中,可以用getattr获取并运行,可以实现相同功能的还有operator.methodcaller()

  1. getattr(f, 'run')() # hello world
  2. print(getattr(f, 'x')) # 1

setattr:设置类/实例的属性/方法

  1. def fun(self):
  2. print('hello shanghai')
  3.  
  4. setattr(Foo, 'fun', fun) # 给Foo类增加fun方法
  5. f.fun()
  6. setattr(f, 'fun', fun) # 给f实例增加fun方法,调用时要把实例传进去
  7. f.fun(f)
  8. setattr(Foo, 'typecode', 'd') # 设置类属性
  9. print(Foo.typecode)
  10. setattr(f, 'name', 'guxh') # 设置类属性
  11. print(f.name)

delattr:删除类/实例的属性/方法

  1. delattr(f, 'x')
  2. print(f.x) # AttributeError

6,构造函数

6.1,简化构造函数

有点类似namedtuple:

  1. class Structure:
  2. _fields = []
  3.  
  4. def __init__(self, *args):
  5. if len(args) != len(self._fields):
  6. raise TypeError('Excepted {} arguments'.format(len(self._fields)))
  7.  
  8. for name, value in zip(self._fields, args):
  9. setattr(self, name, value)
  10.  
  11. class Stock(Structure):
  12. _fields = ['name', 'shares', 'price']
  13.  
  14. s = Stock('IBM', 50, 68.6)
  15. print(s.name) # ‘IBM'
  16. print(s.shares) # 50
  17. print(s.price) # 68.6

注意,这里用的是setattr设置属性的键值对,如果有实例字典__dict__无法适应__slots__(元组保存)和描述符(或property)场景。

6.2,使用@classmethod创建备选构造函数

  1. import time
  2.  
  3. class Date:
  4.  
  5. def __init__(self, year, month, day):
  6. self.year = year
  7. self.month = month
  8. self.day = day
  9.  
  10. @classmethod
  11. def today(cls):
  12. t = time.localtime()
  13. return cls(t.tm_year, t.tm_mon, t.tm_mday)
  14.  
  15. def __repr__(self):
  16. return 'Date({}, {}, {})'.format(self.year, self.month, self.day)
  17.  
  18. a = Date(2019, 10, 1) # 实例a:使用构造函数
  19. b = Date.today() # 实例b:使用备选构造函数

在__init__中使用if...else...虽然也能创造缺省的实例,列入b=Date()时返回today,但是可读性不行

 

guxh的python笔记五:面向对象的更多相关文章

  1. 8.python笔记之面向对象基础

    title: 8.Python笔记之面向对象基础 date: 2016-02-21 15:10:35 tags: Python categories: Python --- 面向对象思维导图 (来自1 ...

  2. guxh的python笔记一:数据类型

    1,基本概念 1.1,数据类型 基本数据类型:字符串,数字,布尔等 引用数据类型:相对不可变(元组),可变(列表,字典,集合等) 基本数据类型存放实际值,引用数据类型存放对象的地址(即引用) ==:判 ...

  3. Python:笔记(3)——面向对象编程

    Python:笔记(3)——面向对象编程 类和面向对象编程 1.类的创建 说明:和Java不同的是,我们不需要显示的说明类的字段属性,并且可以在后面动态的添加. 2.构造函数 构造函数的功能毋庸置疑, ...

  4. guxh的python笔记二:函数基础

    1,函数的参数 1.1,查看函数的参数类型 def run(a, *args, b, **kwargs): return a + b 可以通过如下方式查看参数类型: import inspect k ...

  5. guxh的python笔记三:装饰器

    1,函数作用域 这种情况可以顺利执行: total = 0 def run(): print(total) 这种情况会报错: total = 0 def run(): print(total) tot ...

  6. guxh的python笔记十:包和模块

    1,包和模块 包package:本质就是一个文件夹/目录,必须带一个__init.__.py的文件 模块module:.py结尾的python文件 2,导入方法 import pandas, coll ...

  7. guxh的python笔记四:迭代

    1,可迭代对象iterable,迭代器iterator,生成器generator 可迭代对象iterable: 实现__iter__方法的类.__iter__方法返回iterator或者generat ...

  8. 9.Python笔记之面向对象高级部分

    类的成员 类的成员可以分为三大类:字段.方法和属性 注:所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段.而其他的成员,则都是保存在类中,即:无论对象的 ...

  9. Python笔记之面向对象

    1.类和对象 #create a class class fruit: def say(self): print "hello, python" if __name__ == &q ...

随机推荐

  1. Spring Enable* 注解

    Spring提供了一系列以Enable开头的注解,这些注解本质上是激活Spring的某些管理功能.比如,EnableWebMvc. 这个注解引入了MVC框架在Spring 应用中需要用到的所有bean ...

  2. 从session中获取当前用户的工具类

    package cn.crmx.crm.util; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Ht ...

  3. (备忘)打开office2010总是在配置进度

    1.同时按上键盘上面的windows键和R键,出现“运行” 2.输入“regedit”,回车进入注册表 3.点击“HKEY_CURRENT_USER”展开,依次“Software”--“Microso ...

  4. pandas的Panel类型dtype

    panel = pd.Panel(dataframe_dict) 把一个多列类型不相同(里面有int,float)的dataframe字典直接赋值给Panel,从Panel中解析出来的datafram ...

  5. this inspection detects names that should resolved but don't. Due to dynamic dispatch and duck typing, this is possible in a limited but useful number of cases. Top-level and class-level items are sup

    输入第一行代码:import logging;logging.basicConfig(level==logging.INFO) 提示:this inspection detects names tha ...

  6. shiro学习总结

    首先4个比较好的例子供参考: 1.常规Spring MVC拦截器实现的认证和权限管理例子 https://blog.csdn.net/u013647382/article/details/539956 ...

  7. 前端学习历程--css①

    ---恢复内容开始--- 本文用自己的理解,总结网上或者自身经历的问题,加以汇总,方便查找: 一.浏览器默认样式 1.浏览器处理css&html a.css作用范围:盒子模式.浮动.定位.背景 ...

  8. sublime-text3按tab跳出括号

    功能 通过按tab自动跳过右括号,右引号,虽然也可以按右方向键,但离得太远按起来太麻烦 在首选项->按键绑定里添加: { "keys": ["tab"], ...

  9. sersync 开机自启 (仅供自己参考)

    sersync是一个实时同步的软件,,将其添加到/etc/rc.local中没有效果 ##之所以没效果是因为:####由于/etc/rc.local是/etc/rc.d/rc.local的软连接,所以 ...

  10. week_one-python基础 列表 增删改查

    # Author:larlly #列表增删改查#定义列表name = ["wo","ni","ta","wo"] #定义 ...