本章高级编程中所谓的高级,指的是相对比较抽象,也比较实用的关于类的技术,但对于非强编程的专业,比如数据分析,办公弄自动化等, 可能暂时并不需要, 所以对这一部分同学暂时不推荐学习, 可能您根本不需要,所以我们统一放到了高级编程中.
类属性(propety)
类的属性值(attribute)经常需要被方位, 通常的访问为get和set两个, 即对属性进行复制和读取属性.
如下面案例,我们对属性 name的访问不想让外部直接访问, 这涉及对内容的封装,此时可以通过两个函数setName和 getName来完成, 这就避免了但对属性的直接访问:
# 属性案例
# 创建Student类,描述学生类
# 学生具有Student.name属性
# 但name格式并不统一
# 可以用增加一个函数,然后自动调用的方式,但很蠢
class Student():
def __init__(self, name, age):
self.name = name
self.age = age
# 如果不想修改代码
self.setName(name)
# 介绍下自己
def intro(self):
print("Hai, my name is {0}".format(self.name))
def setName(self, name):
self.name = name.upper()
s1 = Student("LIU Ying", 19.8)
s2 = Student("michi stangle", 24.0)
s1.intro()
s2.intro()
访问结果如下:
Hai, my name is LIU YING
Hai, my name is MICHI STANGLE
此时虽然我们给出了避免类的属性直接被访问的方式, 即通过函数访问,但并不能避免属性真的被外部访问,至少语法上不能避免,比如一样可以 直接通过:
print(s1.name)
来访问属性.
为了避免此类情况发生,我们可以对属性进行改名,比如添加一个下划线_来表明这个属性不希望被外部直接访问,但此种方式只是一个标记,或者叫 约定俗成,如果我访问, 还是没有语法问题的.
类属性值(property)
主要为了满足以下需求,我们设计了类的属性(property)这个功能:
- 封装类的属性值(attribute), 不许外部访问此类内容
- 对类的属性(attribute)赋值的时候, 可能我们有额外的需求,例如一些额外的操作
此时我们可以用一个函数来代替属性值(attribute)
对一个变量的操作,一般具有赋值,一般也就赋值,读取,删除三个操作,所以使用属性(properety)需要的大致步骤是:
- 定义三个函数,即对这个属性(property)进行赋值,读取, 删除时候的操作功能
- 把这三个函数绑定到一个变量名称上, 此后对这个变量名称的访问即调用对于的函数.
具体案例参看下面, 案例中,如果访问Person的name和age属性, 我们希望带有额外的操作, 比如自动进行一些大小写转换 等, 此时可以把name等设置成property来进行操作:
# peroperty案例
# 定义一个Person类,具有name,age属性
# 对于任意输入的姓名,我们希望都用大写方式保存
# 年龄,我们希望内部统一用整数保存
# x = property(fget, fset, fdel, doc)
class Person():
'''
这是一个人,一个高尚的人,一个脱离了低级趣味的人
还他妈的有属性
'''
# 函数的名称可以任意
# 对于此propery读取时候的操作,此时对任意名称的读取, 返回这个名称的两次重复值
def fget(self):
return self._name * 2
# 对于对name进行赋值时候的操作, 即默认把值转换成大写
def fset(self, name):
# 所有输入的姓名以大写形式保存
self._name = name.upper()
# 对于删除时候操作, 删除的时候并不是真正删除,而是把值变成了NoName字符串
def fdel(self):
self._name = "NoName"
# 调用property函数, 把变量 _name 和三个操作函数绑定到属性 name上,
# 以后对name操作即调用相应三个操作函数
name = property(fget, fset, fdel, "对name进行下下操作啦")
# 作业:
# 1. 在用户输入年龄的时候,可以输入整数,小数,浮点数
# 2. 但内部为了数据清洁,我们统一需要保存整数,直接舍去小数点
# 调用类
p1 = Person()
p1.name = "TuLing" # 赋值, 调用fset
print(p1.name) # 读取,调用fget
运行结果如下所示:
TULINGTULING
@property
对上述属性系统还给提供了装饰器来完成相应的工作
单独对某个函数进行修饰后, 则此函数名称就是一个具备只读功能的属性(property), 注意此属性 只能读取,不能有别的功能, 例如下面案例,如果对属性year进行除了读取以外操作, 报错!
代码如下, 代码中只用property修饰, 则属性只具有只读性质:
class Person():
def __init__(self):
self._age = 18
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = "北京图灵学院" + name
@property
def year(self):
return 2022 - self._age
p = Person( )
# 读取year属性
print(p.year)
# year为只读属性, 尝试其他操作报错
p.year = 1997
执行上述代码后, 报错如下:
Connected to pydev debugger (build 211.7142.13)
2004
Traceback (most recent call last):
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/pydevd.py", line 1483, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/Users/bbb/baoshu/book/python/new_python/code/tt.py", line 26, in <module>
p.year = 1997
AttributeError: can't set attribute
python-BaseException
如果想让定义的属性property具有其他能力,比如赋值删除等, 则需要继续时候用装饰器来完成,参考 下面代码,定义的属性name具有删除,赋值,读取功能:
class Person():
def __init__(self):
self._name = "NoName"
@property
def name(self):
return self._name
@name.setter
def name(self, n):
self._name = n.upper()
@name.deleter
def name(self):
self._name = "IsDeleted"
p = Person( )
# 读取name属性
print(p.name)
# 调用setter
p.name = "dana liu"
print(p.name)
# 调用deleter
del p.name
# 调用getter
print(p.name)
代码运行如下:
/Users/bbb/anaconda3/bin/python3 /Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/pydevd.py --multiproc --qt-support=auto --client 127.0.0.1 --port 62518 --file /Users/bbb/baoshu/book/python/new_python/code/tt.py
Connected to pydev debugger (build 211.7142.13)
NoName
DANA LIU
IsDeleted
从上面案例也可以基本推断出, 所谓装饰器就是对上一小节中属性函数的简单包装, 在使用中也是 对赋值,读取,删除三个函数调用而已.
属性(property)的命名务必跟类中使用的属性(attribute)不能相同,否则会造成递归调用,自己想想点解?
类的内置属性
类会内置了很多属性,通过这些属性方便我们对类的使用和控制, 一般此类内置都以双下划线开头结尾, 我们介绍四个内置属性如下:
__dict__: 类或者实例的所有属性(是所有,包括attribute, 函数, property等)__doc__: 类的文档__name__: 类的名称__bases__: 类的父类
上面知识点以类Person为例, 类的实现代码如下:
class Animal():
pass
class Person(Animal):
'''
这是一个人,一个高尚的人,一个脱离了低级趣味的人
还他妈的有属性
'''
# 函数的名称可以任意
# 对于此propery读取时候的操作,此时对任意名称的读取, 返回这个名称的两次重复值
def fget(self):
return self._name * 2
# 对于对name进行赋值时候的操作, 即默认把值转换成大写
def fset(self, name):
# 所有输入的姓名以大写形式保存
self._name = name.upper()
# 对于删除时候操作, 删除的时候并不是真正删除,而是把值变成了NoName字符串
def fdel(self):
self._name = "NoName"
# 调用property函数, 把变量 _name 和三个操作函数绑定到属性 name上,
# 以后对name操作即调用相应三个操作函数
name = property(fget, fset, fdel, "对name进行下下操作啦")
def __init__(self):
self.p_name = "NoName"
self.age = 19
__dict__
如果查看类或者实例的内容, 则可以直接按下面代码打印:
p = Person( )
print(Animal.__dict__)
print("=" * 30)
print(Person.__dict__ )
print("=" * 30)
print( p.__dict__ )
打印结果如下:
Connected to pydev debugger (build 211.7142.13)
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
==============================
{'__module__': '__main__', '__doc__': '\n 这是一个人,一个高尚的人,一个脱离了低级趣味的人\n 还他妈的有属性\n ', 'fget': <function Person.fget at 0x7f8465dce510>, 'fset': <function Person.fset at 0x7f8465dce730>, 'fdel': <function Person.fdel at 0x7f8465dce7b8>, 'name': <property object at 0x7f8465dcac78>, '__init__': <function Person.__init__ at 0x7f8465dce840>}
==============================
{'p_name': 'NoName', 'age': 19}
这里类的属性和实例的属性进行了严格的区分, 这两者内容也大不一样 子类和父类也各自维持各自的
__dict__内容 内置类型list,tuple,dict等没有__dict__属性
可以简单理解成:
- 实例的
__dict__包含的是类定义的时候前面有self.的内容,例如self.age - 类的
__dict__包含的是出去实例__dict__之外的所有
__name__
__name__可以用来代表一段代码的名称,我们在写代码的时候经常写一个入口程序:
if __name__ == "__main__":
pass
作为一段段代码的第一行执行程序, 就是要检查这段代码的名称是否是一个值.
在类中我们对类调用__name__属性的时候代表的是这个类的名字, 不如下面代码:
p = Person( )
print(Animal.__name__)
print("=" * 30)
print(Person.__name__ )
print("=" * 30)
print(__name__)
print("=" * 30)
print(p.__name__)
代码执行结果如下:
Connected to pydev debugger (build 211.7142.13)
Animal
==============================
Person
==============================
__main__
==============================
python-BaseException
Traceback (most recent call last):
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/pydevd.py", line 1483, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/Users/bbb/baoshu/book/python/new_python/code/tt.py", line 45, in <module>
print(p.__name__)
AttributeError: 'Person' object has no attribute '__name__'
代码最后一行执行保存, 因为实例变量p没有__name__属性
__bases__ / __base__
用来表示一个类的父类和父类的列表.
参见下面代码:
p = Person( )
print(Person.__base__)
print(Person.__bases__)
执行结果如下:
Connected to pydev debugger (build 211.7142.13)
<class '__main__.Animal'>
(<class '__main__.Animal'>,)
魔法函数
类的魔法函数是指一类特殊的函数:
- 此类函数由类定义名称, 不可以随意命名
- 一般在某个时机会被自动调用,不需要手动调用
- 每个魔法函数都对于一个固定的调用时机, 通常也默认有固定的的功能和写法, 如果实现, 则时机到了会被自动调用
- 名称由前后双下划线包裹
我们以前介绍的构造函数就是一个魔法函数, 类实例化的时候被调用,具有特定的写法和命名等.
本章我们介绍其他常用的魔法函数.
__call__
实例可以直接被调用, 此时会执行__call__函数:
class A():
def __init__(self, name = 0):
print("哈哈,我被调用了")
def __call__(self):
print("我被调用了again")
定义后用下面两行代码,第一行生成实例,第二行直接运行实例,此时调用魔法函数:
a = A()
a()
运行结果如下:
哈哈,我被调用了
我被调用了again
__str__
当实例被需要当做字符串处理的时候会调用此函数, 如果没有实现此函数会执行默认功能.
此函数要求返回一个字符串.
class A():
def __init__(self, name = 0):
print("哈哈,我被调用了")
def __call__(self):
print("我被调用了again")
def __str__(self):
return "图灵学院的例子"
a = A()
print(a)
在执行print(a)的时候, 需要把实例转成一个字符串,此时会执行上面的魔法函数, 上面魔法函数返回的内容 就是打印出来的内容.
哈哈,我被调用了
图灵学院的例子
__geattr__
调用某个属性的时候, 如果这个属性不存在, 会自动执行这个魔法函数.
魔法函数的返回值作为不存在的这个属性的值.
class A():
name = "NoName"
age = 18
def __getattr__(self, name):
print("没找到呀没找到")
print(name)
定义完后执行下面代码:
a = A()
print(a.name)
print(a.addr)
# 作业:
# 为什么会打印第四句话,而且第四句话是打印的 None
# 答案在QQ群9990960
执行结果如下:
NoName
没找到呀没找到
addr
None
__setattr__
当给类的属性进行赋值的时候, 自动调用此函数.
需要注意此函数的写法,比较特殊 魔法函数的很多写法都比较特殊,性质决定
参考案例:
class Person():
def __init__(self):
pass
def __setattr__(self, name, value):
print("设置属性: {0}".format(name))
# 下面语句会导致问题,死循环
#self.name = value
# 此种情况,为了避免死循环,规定统一调用父类魔法函数
super().__setattr__(name, value)
调用上面定义的类:
p = Person()
print(p.__dict__)
p.age = 18
print(p.__dict__)
结果如下:
Connected to pydev debugger (build 211.7142.13)
{}
设置属性: age
{'age': 18}
__gt__
此类魔法函数在比较两个类实例的时候会被自动调用,除了大于,小于,还有大于等于,小于等于, 等于,不等于等.
参看下面代码, 如果比较两个学生类的实例, 则以魔法函数返回的结果为比较的结果:
class Student():
def __init__(self, name):
self._name = name
# 比较两个实例按名称比较
def __gt__(self, obj):
print("哈哈, {0} 会比 {1} 大吗?".format(self, obj))
return self._name > obj._name 调用代码如下:
# 作业:
# 字符串的比较是按什么规则
stu1 = Student("one")
stu2 = Student("two")
print(stu1 > stu2)
执行结果如下:
# 作业:
# 下面显示结果不太美观,能否改成形如 "哈哈, one 会比 two 大吗?“
```
哈哈, <__main__.Student object at 0x7f4aac6b3b00> 会比 <__main__.Student object at 0x7f4aac6b3ac8> 大吗?
False
抽象类
有些类不需要定义实例, 这些类存在的意义就是被继承, 这些类我们可以定义成抽象类,即只需要被继承不能够 被实例化的类.
-
定义抽象类需要借助
abc模块, 即抽象类是abc.ABCMeta的子类. -
抽象类包含至少一个抽象函数, 此时需要用到装饰器
@abc.abstractxxxxx来定义一个抽象函数, 此类 函数不需要有实现代码, 只能被继承, 然后在子类中实现具体功能 -
一个包含抽象函数的类就是抽象类, 哪怕只包含一个抽象函数, 则这个类就是抽象的,不能被实例化
-
@abc.abstractxxxx装饰器只是说你这个函数是的性质,属于抽象的且是xxxx函数,包括abstractmethode: 普通抽象函数abstractclassmethode:抽象类函数abstractstaticmethode: 抽象静态函数abstractproperty: 抽象属性
参看代码,从一个了一个人类, 因为人类具有抽象函数(还不止一个), 所以是一个抽象类
# 抽象类的实现
import abc
#声明一个类并且指定当前类的元类
class Human(metaclass=abc.ABCMeta):
# 定义一个抽象的方法
@abc.abstractmethod
def smoking(self):
pass
# 定义类抽象方法
@abc.abstractclassmethod
def drink():
pass
# 定义静态抽象方法
@abc.abstractstaticmethod
def play():
pass
def sleep(self):
print("Sleeping.......")
此时直接用Human实例化是会报错的:
Connected to pydev debugger (build 211.7142.13)
Traceback (most recent call last):
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/pydevd.py", line 1483, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/Users/bbb/baoshu/book/python/new_python/code/tt.py", line 22, in <module>
h = Human()
TypeError: Can't instantiate abstract class Human with abstract methods drink, play, smoking
python-BaseException
元类
手动组装类
函数可以当做换一个变量处理,此时函数可以当做值来传递,这是我们在函数中已经给大家讲过的内容, 在类中定义的函数,包括 实例函数,类函数, 静态函数等,都可以看做一个变量来处理,即我们可以灵活的组装一个类.
参看下面代码, 我们定义了一个类A, 但没有具体内容, 同时定义了函数say, 这就是一个普通函数,我们可以单独调用, 也可以把这个函数作为值传递给A中的某一个变量, 此时可以把函数say作为类的函数调用,同时在A的实例调用的时候, 第一个参数会自动把自己传入, 此时如果想把say当做实例函数,必须至少有一个参数,否则报错:
class A():
pass
def say(self):
print("Saying... ...")
class B():
def say(self):
print("Saying......")
# 单独调用say'必须有对参数,否则报错
say(9)
# 给类A添加一个实例函数say
A.say = say
a = A()
a.say()
# 打印出A的内容
print(A.__dict__)
上述代码运行结果如下:
onnected to pydev debugger (build 211.7142.13) Saying… … Saying… … {‘module’: ‘main’, ‘dict’: <attribute ‘dict’ of ‘A’ objects>, ‘weakref’: <attribute ‘weakref’ of ‘A’ objects>, ‘doc’: None, ‘say’: <function say at 0x7f83e28e6620>}
可以看出, 我们通过后期的赋值,可以随意赋予一个类以函数
这类赋值不可以直接对类实例进行,例如下面代码:
class A():
pass
def say(self):
print("Saying... ...")
a1 = A()
a2 = A()
a1.say = say
# 此时say作为a`是一个普通函数, 不是类中定义的
# 调用可以这样
a1.say(a1)
# 但如果是按照实例方法调用则报错
a1.say()
运行结果如下:
Connected to pydev debugger (build 211.7142.13)
Saying... ...
Traceback (most recent call last):
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/pydevd.py", line 1483, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/Users/bbb/baoshu/book/python/new_python/code/tt.py", line 19, in <module>
a1.say()
TypeError: say() missing 1 required positional argument: 'self'
python-BaseException
MethodType 组装类
MethodType方法可以给实例对象或类绑定方法, 功能同上面全手动类似.
下面代码给实例利用MethodType增加函数功能, 但此时并没有真正修改类A的内容, 所以对实例a2修改后 面的代码a1,a3再次调用say就会报错.
# 自己组装一个类
from types import MethodType
class A():
pass
class B():
pass
def say(self):
print("Saying... ...")
a1 = A()
a2 = A()
a2.say = MethodType(say, A)
a2.say()
a3 = A()
# 以下使用都会报错, 报错信息为A没有say属性
a3.say()
a1.say()
代码运行后结果如下:
Connected to pydev debugger (build 211.7142.13)
Saying... ...
python-BaseException
Traceback (most recent call last):
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/pydevd.py", line 1483, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "/Applications/PyCharm.app/Contents/plugins/python/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/Users/bbb/baoshu/book/python/new_python/code/tt.py", line 24, in <module>
a3.say()
AttributeError: 'A' object has no attribute 'say'Vk
如果想直接修改类的定义内容,则需要直接给类赋值,代码如下:
# 自己组装一个类
from types import MethodType
class A():
pass
def say(self):
print("Saying... ...")
A.say = MethodType(say, A)
a1 = A()
a2 = A()
a3 = A()
a1.say()
a2.say()
a3.say()
上面代码修改了类的内容,以后由这个类产生的实例都会具备相应功能, 运行结果如下:
Connected to pydev debugger (build 211.7142.13)
Saying... ...
Saying... ...
Saying... ...
利用type来创造类
python中只有type才是唯一真神, 他可以创建万物,包括类.
我们可以利用这来完全定义类, 参看下代码:
# 先定义类应该具有的成员函数
def say(self):
print("Saying.....")
def talk(self):
print("Talking .....")
# 用type来创建一个类
# thype的参数含义, 1,类名称, 2.类的父类们, 3, 类的内容, 字典形式
A = type("AName", (object,), {"class_say": say, "class_talk": talk})
# 然后可以像正常访问一样使用类
a = A()
a.class_say()
a.class_talk()
print(a.class_say)
print(a.class_talk)
上例中,我们用type无中生有了一个类, 同时用这个类创建实例并运行, 味道好极了, 运行结果如下:
Connected to pydev debugger (build 211.7142.13)
Saying.....
Talking .....
<bound method say of <__main__.AName object at 0x7fe70cfe6be0>>
<bound method talk of <__main__.AName object at 0x7fe70cfe6be0>>
元类
元类是负责创建类的类, 叫做元类.
我们上面使用各种方法, 理论上也可以创建一个类,但元类是专业负责创建类的一个有着特殊写法的类.
元类写法固定:
- 必须继承自
type - 一般以
MetaClass结尾 - 实现内容和步骤有规定,要实现
__new__魔法函数
其实元类就是一个批量创建类的模板
参看下面代码, 我们实现一个元类, 负责创建其余的类.
# 元类演示
# 元类写法是固定的,必须继承自type
# 元类一般命名以MetaClass结尾
class TulingMetaClass(type):
# 注意以下写法
def __new__(cls, name, bases, attrs):
# 自己的业务处理
print("哈哈,我是元类呀")
attrs['id'] = '000000'
attrs['addr'] = "北京海淀区公主坟西翠路12号"
attrs['douyin'] = "liudana"
attrs['wechat'] = "131 191 44 223"
# 固定返回
return type.__new__(cls, name, bases, attrs)
# 元类定义完就可以使用,使用注意写法
# 必须表明metaclass属性, 即指明利用哪个元类创建这个类
class Teacher(object, metaclass=TulingMetaClass):
pass
t = Teacher()
print(t.id)
print(t.douyin)
print(t.wechat)
上面代码运行解雇如下:
Connected to pydev debugger (build 211.7142.13)
哈哈,我是元类呀
000000
liudana
131 191 44 223
迷信(Mixin)
Mixin这种设计模式表达的意思是掺入.
python类的血统比较乱,不拒绝多继承,这就导致一些继承方面的问题(菱形继承), 一个人爸爸太多了总有种说不出的感觉, 代码也是, 让人混乱,为了解决此类问题, 我们不在推荐退继承, 但需要功能拓展怎么办? 我们用迷信(Mixin)处理.
迷信的宗旨就是把父类和子类的耦合关系降到最低,甚至是零, 迷信本质上还是父类,但这个父类本质上只负责处理业务, 完全做到了跟子类除了 业务外代码没任何耦合度(或者尽量减少耦合度).
Mixin的使用原则是:
- 首先他必须表示某一单一功能,而不是某个物品
- 职责必须单一,如果由多个功能,则写多个
Mixin Mixin不能依赖于子类的实现- 子类即使没有继承这个
Mixin类, 也能照样工作,只是缺少了某个功能 - Mixin类中不应该显式的出现__init__初始化方法
- Mixin类通常不能独立工作,因为它是准备混入别的类中的部分功能实现
- Mixin类的祖先类也应是Mixin类
- 使用时,Mixin类通常在继承列表的第一个位置
- 父类只做方法的定义
- 抽象类,抽象方法
- 其他语言抽象类不可实例化
- 抽象类一般不实例化,作为基类用
使用Mixin的有点:
- 使用Mixin可以在不对类进行任何修改的情况下,扩充功能
- 可以方便的组织和维护不同功能组件的划分
- 可以根据需要任意调整功能类的组合
- 可以避免创建很多新的类,导致类的继承混乱
下面代码中所谓的迷信完全就是父类,只是跟子类或者其他内容的耦合度很低:
# MiuIn案例
class Person(object):
pass
class FWordMixin():
def fxxk(self):
print("这特么地........")
class HappyMixin():
def happy(self):
print("娃哈哈哈哈哈哈........")
class TeacherWithNothing(Person):
'''
一个冰冷的莫得感情的老师
'''
name = "别的老师"
def work(self):
print("上课啦.....")
class TeacherWithHappy(Person, HappyMixin):
'''
一个快乐的老师
'''
name = "别的老师"
def work(self):
print("上课啦.....")
class LiuDana(Person, FWordMixin):
'''
大拿上课老骂人, 这不好,阿弥陀佛
'''
name = "别的老师"
def work(self):
print("上课啦.....")
nothing = TeacherWithNothing()
nothing.work()
happy = TeacherWithHappy()
happy.work()
happy.happy()
dana = LiuDana()
dana.fxxk()
运行结果如下:
Connected to pydev debugger (build 211.7142.13)
上课啦.....
上课啦.....
娃哈哈哈哈哈哈........
这特么地........
上面代码可以看出, 老师类的高兴和骂人功能,只是一个迷信,如果需要,只是简单的继承自这个迷信即可.
迷信就是特殊的父类,且一般不把他看做父类,只看着功能的横向扩展
类相关函数
在对类的使用中, 我们可能需要对类进行判断甚至控制,此时我们可以借助于下面的一些函数进行.
issubclass:检测一个类是否是另一个类的子类isinstance:检测一个对象是否是一个类的实例hasattr:检测一个对象是否由成员xxxgetattr: get attributesetattr: set attributedelattr: delete attributedir: 获取对象的成员列表
代码简单,参看下面案例即可:
class A():
pass
class B(A):
def __init__(self):
self.name = "刘大拿"
self.tel = '131 191 44223'
self.qq_group = "9990960"
def say(self):
print("Hello ......")
# 第一个参数是判断的类名, 第二个参数是可能的父类组成的tuple`
print("B是A的子类: ", issubclass(B, (A, )))
b = B()
print('b是B的实例:', isinstance(b, B))
# 第二个参数字符串形式的属性名称
print("B是否具有属性say:", hasattr(b, "say"))
# 利用字符串得到某个具体的函数或者功能
# 这个在系统级别的代码中常用
say = getattr(b, "say")
# 注意这个调动方式, say必须要求有一个参数,但此时这个函数代表的是b实例的函数say,此种方式调用照样默认传入b作为第一个参数
say()
# 给实例或者类设置内容
b = B()
# 给实例b设置一个属性age
setattr(b, "age", 18)
print(b.age)
def sayage(self, age):
print("AGE.......", age)
# 同样可以把函数设置给b,但此时这个函数不作为b的实例函数, 调用的时候也不默认把自身当做第一个参数传入
setattr(b, "sayage", sayage)
b.sayage(b, 23)
# 显示类,实例所有可用内容,属性
print(dir(b))
print(dir(B))