语言的分类
面向机器
抽象成机器指令,机器容易理解
代表:汇编语言
面向过程
做一件事情,排出个步骤,第一步干什么,第二步干什么,如果出现情况A,做什么处理,如果出现了情况B,做什么处理。
问题规模小,可以步骤化,按部就班处理。
代表:C语言
面向对象OOP
随着计算机需要解决的问题的规模扩大,情况越来越复杂。需要很多人、很多部门协作,面向过程编程不太适合了。
代表:C++、Java、Python等
面向对象 什么是面向对象呢?
一种认识世界、分析世界的方法论。将万事万物抽象为类。
类class
类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合。
用计算机语言来描述类,就是属性和方法的集合。
对象instance
、object
对象是类的具象,是一个实例。
对于我们每个人这个个体,都是抽象概念人类的不同的实例。
举例:
你吃鱼
你,就是对象;鱼,也是对象;吃就是动作
你是具体的人,是具体的对象。你属于人类,人类是个抽象的概念,是无数具体的个体的抽象。鱼,也是具体的对象,就是你吃的这一条具体的鱼。这条鱼属于鱼类,是无数的鱼抽象出来的概念。
吃,是动作,也是操作,也是方法,这个吃是你的动作,也就是人类具有的方法。如果反过来,鱼吃人。吃就是鱼类的动作了。
吃,这个动作,很多动物都具有的动作,人类和鱼类都属于动物类,而动物类是抽象的概念,是动物都有吃的动作,但是吃法不同而已。
你驾驶车,这个车也是车类的具体的对象(实例),驾驶这个动作是鱼类不具有的,是人类具有的方法。
属性,它是对象状态的抽象,用数据结构来描述。
操作,它是对象行为的抽象,用操作名和实现该操作的方法来描述
每个人都有名字、身高、体重等信息,这些信息是个人的属性,但是,这些信息不能保存在人类中,因为它是抽象的概念,不能保留具体的值。
而人类的实例,是具体的人,他可以存储这些具体的属性,而且可以不同人有不同的属性。
哲学
一切皆对象
对象是数据和操作的封装
对象是独立的,但是对象之间可以相互作用(这对应的是人吃鱼的例子,动作把这两个对象关联起来 )
目前OOP是最接近人类认知的编程范式
面向对象三要素
封装
组装:将数据和操作组装到一起。
隐藏数据:对外只暴露一些接口,通过接口访问对象。比如驾驶员使用汽车,不需要了解汽车的构造细节,只需要知道使用什么部件怎么驾驶就行,踩了油门就能跑,可以不了解后面的机动原理。
继承
多复用,继承来的就不用自己写了
多继承少修改,OCP(Open-closed Principle),使用继承来改变,来体现个性
多态
人类就是封装:
人类继承自动物类,孩子继承父母特征。分为单一继承、多继承;
多态,继承自动物类的人类、猫类的操作“吃”不同
Python的类 定义
必须使用class
关键字
类名必须是用大驼峰命名
类定义完成后,就产生了一个类对象,绑定到了标识符ClassName
上
举例
1 2 3 4 5 6 7 8 9 10 class MyClass : """A example class""" x = 'abc' def foo (self ): return 'My Class' print (MyClass.x)print (MyClass.foo)print (MyClass.__doc__)
类对象及类属性
类对象,类的定义就会生成一个类对象
类的属性,类定义中的变量和类中定义的方法都是类的属性
类变量,上例中x
是类MyClass
的变量
MyClass
中,x
、foo
都是类的属性,__doc__
也是类的属性
foo
方法是类的属性,如同吃
是人类的方法,但是每一个具体的人才能吃东西,也就是说吃
是人的实例才能调用的方法
foo
是方法对象method
,不是普通的函数对象function
了,它一般要求至少有一个参数。第一个参数可以是self
(self
只是个惯用标识符,可以换名字),这个参数位置就留给了self
。
self
指代当前实例本身
问题
上例中,类是谁?实例是谁?
实例化
使用上面的语法,在类对象名称后面加上一个括号,就调用类的实例化方法,完成实例化。实例化就真正创建一个该类的对象(实例)。例如
1 2 tom = Person() jerry = Person()
上面的tom
、jerry
都是Person
类的实例,通过实例化生成了2个实例。
每次实例化后获得的实例,是不同的实例,即使是使用同样的参数实例化,也得到不一样的对象。
Python类实例化后,会自动调用 __init__
方法。这个方法第一个参数必须留给self
,其它参数随意。
__init__
方法MyClass()
实际上调用的是__init__(self)
方法,可以不定义,如果没有定义会在实例化后隐式调用。
作用:对实例进行初始化
1 2 3 4 5 6 7 class MyClass: def __init__(self): print('init') print(MyClass) # 不会调用 print(MyClass()) # 调用__init__ a = MyClass() # 调用__init__
初始化函数可以有多个参数,请注意第一个位置必须是self,例如__init__(self, name, age)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Person : def __init__ (self, name, age ): self.name = name self.age = age def showage (self ): print ('{} is {}' .format (self.name, self.age)) tom = Person('Tom' , 20 ) jerry = Person('Je' , 25 ) print (tom.name, jerry.age)jerry.age += 1 print (jerry.age)jerry.showage() 输出: Tom 25 26 Jerry is 26
注意:__init__()
方法不能有返回值,也就是只能是None
实例对象instance
类实例化后一定会获得一个对象,就是实例对象
上例中的tom
、jerry
就是Person
类的实例
__init__
方法的第一参数self
就是指代某一个实例。
类实例化后,得到一个实例对象(实例对象就是上面说的类的对象),实例对象会绑定方法,调用方法时采用jerry.showage()
的方式。但是函数签名是showage(self)
,少传一个参数self
吗?
这个self
就是jerry
,Python会把方法的调用者作为第一参数self
的实参传入。
self.name
就是jerry
对象的name
,name
是保存在了jerry
对象上,而不是Person
类上。所以,称为实例变量。
self
1 2 3 4 5 6 7 8 9 10 class MyClass : def __init__ (self ): print ('self in init = {}' .format (id (self))) c = MyClass() print ('c = {}' .format (id (c)))self in init = 139837178141328 c = 139837178141328
上例说明,self
就是调用者,就是c
对应的实例对象。
self
这个名字只是一个惯例,它可以修改,但是请不要修改,否则影响代码的可读性
看打印的结果,思考一下执行的顺序,为什么?
实例变量和类变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Person : age = 3 def __init__ (self, name ): self.name = name tom = Person('Tom' ) jerry = Person('Jerry' ) print (tom.name, tom.age)print (jerry.name, jerry.age)print (Person.age)Person.age = 30 print (Person.age, tom.age, jerry.age)Tom 3 Jerry 3 3 30 30 30
实例变量是每一个实例自己的变量,是自己独有的;类变量是类的变量,是类的所有实例共享的属性和方法
特殊属性
含义
__name__
对象名
__class__
对象的类型
__dict__
对象的属性的字典
__qualname__
类的限定名
注意:
Python中每一种对象都拥有不同的属性。函数、类都是对象,类的实例也是对象。
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Person : age = 3 def __init__ (self, name ): self.name = name print ('---------class------------' )print (Person.__class__)print (sorted (Person.__dict__.items()), end='\n\n' ) tom = Person('Tom' ) print ('----------instance tom-----------' )print (tom.__class__)print (sorted (tom.__dict__.items()), end='\n\n' )print ("-------------tom's class--------------" )print (tom.__class__.__name__)print (sorted (tom.__class__.__dict__.items()), end='\n\n' )输出: ---------class ------------- <class 'type' > [('__dict__' , <attribute '__dict__' of 'Person' objects>), ('__doc__' , None ), ('__init__' , <function Person.__init__ at 0x7f8b8f126550 >), ('__module__' , '__main__' ), ('__weakref__' , <attribute '__weakref__' of 'Person' objects>), ('age' , 3 )] ----------instance tom------------------ <class '__main__.Person' > [('name' , 'Tom' )] --------------------tom's class---------------------- Person [(' __dict__', <attribute ' __dict__' of ' Person' objects>), (' __doc__', None), (' __init__', <function Person.__init__ at 0x7f8b8f126550>), (' __module__', ' __main__'), (' __weakref__', <attribute ' __weakref__' of ' Person' objects>), (' age', 3)]
上例中,可以看到类属性保存在类的__dict__
中,实例属性保存在实例的__dict__
中,如果从实例访问类的属性,就需要借助__class__
找到所属的类
有了上面知识,再看下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Person : age = 3 height = 170 def __init__ (self, name, age=18 ): self.name = name self.age = age tom = Person('Tom' ) jerry = Person('Jerry' , 20 ) Person.age = 30 print (Person.age, tom.age, jerry.age) print (Person.height, tom.height, jerry.height) jerry.height = 175 print (Person.height, tom.height, jerry.height) tom.height += 10 print (Person.height, tom.height, jerry.height) Person.height += 15 print (Person.height, tom.height, jerry.height) Person.weight += 70 print (Person.weight, tom.weight, jerry.weight) print (tom.__dict__['height' ])print (tom.__dict__['weight' ]) 输出: 30 18 20 170 170 170 170 170 175 170 180 175 185 180 175 Traceback (most recent call last): File "/home/shouyu/PycharmProjects/test/面向对象.py" , line 91 , in <module> Person.weight += 70 AttributeError: type object 'Person' has no attribute 'weight
总结
是类的,也是这个类所有实例的,其实例都可以访问到;是实例的,就是这个实例自己的,通过类访问不到。
类变量是属于类的变量,这个类的所有实例可以共享这个变量。
实例可以动态的给自己增加一个属性。实例.__dict__[变量名]
和实例.变量名
都可以访问到。
实例的同名变量会隐藏这个类变量,或者说是覆盖了这个类变量。这就像上例中用jerry.height = 175
,这样就用jerry的height
覆盖了类中的height
的值
实例属性的查找顺序
指的是实例使用.
来访问属性,会先找自己的__dict__
,如果没有,然后通过属性__class__
找到自己的类,再去类的__dict__
中找
注意,如果实例使用__dict_[变量名]
访问变量,将不会按照上面的查找顺序找变量了,这是指明使用字典的key查找,不是属性查找。
一般来说,类变量使用全大写来命名
装饰一个类 回顾,什么是高阶函数?什么是装饰器函数?
思考,如何装饰一个类?
需求,为一个类通过装饰,增加一些类属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def add_name (name, cls ): cls.NAME = name def add_name (name ): def wrapper (cls ): cls.NAME = name return cls return wrapper @add_name('Tom' ) class Person : AGE = 3 print (Person.NAME)输出: Tom
之所以能够装饰,本质上是为类对象动态的添加了一个属性,而Person这个标识符指向这个类对象。
类方法和静态方法 前面的例子中定义的__init__
等方法,这些方法本身都是类的属性,第一个参数必须是self
,而self
必须指向一个对象,也就是类必须实例化之后,由实例来调用这个方法。
普通函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Person : def normal_method (): print ('normal' ) Person.normal_method() Person().normal_method() print (Person.__dict__)输出: normal Traceback (most recent call last): File "/home/shouyu/PycharmProjects/test/面向对象.py" , line 117 , in <module> Person().normal_method() TypeError: normal_method() takes 0 positional arguments but 1 was given
Person.normal_method()
可以,因为这个方法只是被Person这个名词空间管理的一个普通的方法,normal_method
只是Person
的一个属性而已。
由于normal_method
在定义的时候没有指定self
,所以不能完成实例对象的绑定,不能用Person().normal_method()
调用。
注意:虽然语法是对的,但是,没有人这么用,也就是说禁止这么写
类方法 1 2 3 4 5 6 7 8 9 10 11 12 class Person : @classmethod def class_method (cls ): print ('class = {0.__name__}({0})' .format (cls)) cls.HEIGHT = 170 Person.class_method() print (Person.__dict__)输出: class = Person(<class '__main__.Person' >){'__module__' : '__main__' , 'class_method' : <classmethod object at 0x7fc4283d1a90 >, '__dict__' : <attribute '__dict__' of 'Person' objects>, '__weakref__' : <attribute '__weakref__' of 'Person' objects>, '__doc__' : None , 'HEIGHT' : 170 }
类方法
在类定义中,使用@classmethod
装饰器修饰的方法
必须至少有一个参数,且第一个参数留给了cls
,cls
指代调用者即类对象自身
cls
这个标识符可以是任意合法名称,但是为了易读,请不要修改
通过cls
可以直接操作类的属性
注意:无法通过cls
操作类的实例。为什么?
类方法,类似于C++、Java中的静态方法
静态方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person : @classmethod def class_method (cls ): print ('class = {0.__name__}({0})' .format (cls)) cls.HEIGHT = 170 @staticmethod def static_methd (): print (Person.HEIGHT) Person.class_method() Person.static_methd() print (Person.__dict__)输出: class = Person(<class '__main__.Person' >)170 {'__module__' : '__main__' , 'class_method' : <classmethod object at 0x7f4247ddda90 >, 'static_methd' : <staticmethod object at 0x7f4247df75b0 >, '__dict__' : <attribute '__dict__' of 'Person' objects>, '__weakref__' : <attribute '__weakref__' of 'Person' objects>, '__doc__' : None , 'HEIGHT' : 170 }
静态方法
在类定义中,使用@staticmethod装饰器修饰的方法
调用时,不会隐式的传入参数
静态方法,只是表明这个方法属于这个名词空间。函数归在一起,方便组织管理。
方法调用 类可以定义这么多种方法,究竟如何调用它们?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 class Person : def normal_method (): print ('normal' ) def method (self ): print ("{}'s method" .format (self)) @classmethod def class_method (cls ): print ('class = {0.__name__}({0})' .format (cls)) cls.HEIGHT = 170 @staticmethod def static_methd (): print (Person.HEIGHT) print ('~~~~~~类访问' )print (1 , Person.normal_method()) print (3 , Person.class_method()) print (4 , Person.static_methd) print (Person.__dict__)print ('~~~~~~实例访问' )print ('tom-----' )tom = Person() print (2 , tom.method()) print (3 , tom.class_method()) print (4 , tom.static_methd()) print ('jerry-----' )jerry = Person() print (2 , jerry.method()) print (3 , jerry.class_method()) print (4 , jerry.static_methd()) 输出: ~~~~~~~~~~类访问 normal 1 None class = Person(<class '__main__.Person' >)3 None 170 4 None {'__module__' : '__main__' , 'normal_method' : <function Person.normal_method at 0x7fe2e6af1550 >, 'method' : <function Person.method at 0x7fe2e6af15e0 >, 'class_method' : <classmethod object at 0x7fe2e6b54a90 >, 'static_method' : <staticmethod object at 0x7fe2e6b795b0 >, '__dict__' : <attribute '__dict__' of 'Person' objects>, '__weakref__' : <attribute '__weakref__' of 'Person' objects>, '__doc__' : None , 'HEIGHT' : 170 } ~~~~~~~~~~实例访问 tom------------ <__main__.Person object at 0x7fe2e6b6e8b0 >'s method 2 None class = Person(<class ' __main__.Person'>) 3 None 170 4 None jerry----------- <__main__.Person object at 0x7fe2e6bb7580>' s method2 None class = Person(<class '__main__.Person' >)3 None 170 4 None
类几乎可以调用所有内部定义的方法,但是调用普通的方法
时会报错,原因是第一参数必须是类的实例。
实例也几乎可以调用所有的方法,普通的函数
的调用一般不可能出现,因为不允许这么定义。
总结:
类除了普通方法都可以调用,普通方法需要对象的实例作为第一参数。
实例可以调用所有类中定义的方法(包括类方法、静态方法),普通方法传入实例自身,静态方法和类方法需要找到实例的类。
访问控制 私有(Private)属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person : def __init__ (self, name, age=18 ): self.name = name self.age = age def growup (self, i=1 ): if i > 0 and i < 150 : self.age += i p1 = Person('tom' ) p1.growup(20 ) print (p1.age)p1.age = 160 print (p1.age)输出: 38 160
上例,本来是想通过方法控制属性,但是由于属性在外部可以访问,或者说可见,就可以直接绕过方法,直接修改这个属性。
Python提供了私有属性可以解决这个问题
私有属性
使用双下划线开头的属性名,就是私有属性
1 2 3 4 5 6 7 8 9 10 11 12 class Person : def __init__ (self, name, age=18 ): self.name = name self.__age = age def growup (self, i=1 ): if i > 0 and i < 150 : self.__age += i p1 = Person('tom' ) p1.growup(20 ) print (p1.__age)
通过实验可以看出,外部已经访问不到__age
了,age
根本就没有定义,更是访问不到。那么,如何访问这个私有变量__age
呢?
使用方法来访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Person : def __init__ (self, name, age=18 ): self.name = name self.__age = age def growup (self, i=1 ): if i > 0 and i < 150 : self.__age += i def getage (self ): return self.__age print (Person('tom' ).getage())输出: 18
私有变量的本质 外部访问不到,能够动态增加一个__age
呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Person : def __init__ (self, name, age=18 ): self.name = name self.__age = age def growup (self, i=1 ): if i > 0 and i < 150 : self.__age += i def getage (self ): return self.__age p1 = Person('tom' ) p1.growup(20 ) p1.__age = 28 print (p1.__age)print (p1.getage())print (p1.__dict__)输出: 28 38 {'name' : 'Tom' , '_Person__age' : 38 , '__age' : 28 }
秘密都在__dict__
中,里面是{'__age':28,'_Person_age':38,'name':'tom'}
私有变量的本质:
类定义的时候,如果声明一个实例变量的时候,使用双下划线,Python解释器会将其改名,转换名称为_类名_变量名
的名称,所以用原来的名字访问不到了。
知道了这个名字,能否直接修改呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Person : def __init__ (self, name, age=18 ): self.name = name self.__age = age def growup (self, i=1 ): if i > 0 and i < 150 : self.__age += i def getage (self ): return self.__age p1 = Person('tom' ) p1.growup(20 ) p1.__age = 28 print (p1.__age)print (p1.getage())print (p1.__dict__)p1._Person__age = 15 print (p1.getage())print (p1.__dict__)输出: 15 {'name' : 'Tom' , '_Person__age' : 15 , '__age' : 28 }
从上例可以看出,知道了私有变量的新名称,就可以直接从外部访问到,并可以修改它。
保护变量 在变量名前使用一个下划线,称为保护变量
1 2 3 4 5 6 7 8 9 10 11 class Person : def __init__ (self, name, age=18 ): self.name = name self._age = age tom = Person('Tom' ) print (tom._age)print (tom.__dict__)输出: 18 {'name' : 'Tom' , '_age' : 18 }
可以看出,这个_age
属性根本就没有改变名称,和普通的属性一样,解释器不做任何特殊处理。这只是开发者共同的约定,看见这种变量,就如同私有变量,不要直接使用。
私有方法 参照保护变量、私有变量,使用单下划线、双下划线命名方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Person : def __init__ (self, name, age=18 ): self.name = name self._age = age def _getname (self ): return self.name def __getage (self ): return self._age tom = Person('Tom' ) print (tom._getname()) print (tom.__getage()) print (tom.__dict__)print (tom.__class__.__dict__)print (tom._Person__getage()) 输出: Tom {'name' : 'Tom' , '_age' : 18 } {'__module__' : '__main__' , '__init__' : <function Person.__init__ at 0x7fa7d0a34550 >, '_getname' : <function Person._getname at 0x7fa7d0a345e0 >, '_Person__getage' : <function Person.__getage at 0x7fa7d0a34670 >, '__dict__' : <attribute '__dict__' of 'Person' objects>, '__weakref__' : <attribute '__weakref__' of 'Person' objects>, '__doc__' : None } 18
私有方法的本质 单下划线的方法只是开发者之间的约定,解释器不做任何改变。
双下划线的方法,是私有方法,解释器会改名,改名策略和私有变量相同,_类名__方法名
。方法变量都在类的__dict__
中可以找到。
私有成员的总结 在Python中使用_
单下划线或者__
双下划线来标识一个成员被保护或者被私有化隐藏起来。但是,不管使用什么样的访问控制,都不能真正的阻止用户修改类的成员。Python中没有绝对的安全的保护成员或者私有成员。
因此,前导的下划线只是一种警告或者提醒,请遵守这个约定。除非真有必要,不要修改或者使用保护成员或者私有成员,更不要修改它们。
补丁 可以通过修改或者替换类的成员。使用者调用的方式没有改变,但是,类提供的功能可能已经改变了。
猴子补丁(Monkey Patch):
在运行时,对属性、方法、函数等进行动态替换。
其目的往往是为了通过替换、修改来增强、扩展原有代码的能力。
黑魔法,慎用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from test2 import Personfrom test3 import get_scoredef monkeypatch4Person (): Person.get_score = get_score monkeypatch4Person() if __name__ == "__main__" : print (Person().get_score()) 输出: {'name' : 'Person' , 'English' : 88 , 'Chinese' : 90 , 'History' : 85 } class Person : def get_score (self ): ret = {'English' :78 , 'Chinese' :86 , 'History' :82 } return ret def get_score (self ): return dict (name=self.__class__.__name__,English=88 , Chinese=90 , History=85 )
上例中,假设Person
类get_score
方法是从数据库拿数据,但是测试的时候,不方便。
使用猴子补丁,替换了get_score
方法,返回模拟的数据。
属性装饰器 一般好的设计是:把实例的属性保护起来,不让外部直接访问,外部使用getter
读取属性和setter
方法设置属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Person : def __init__ (self, name, age=18 ): self.name = name self.__age = age def age (self ): return self.__age def set_age (self, age ): self.__age = age tom = Person('Tom' ) print (tom.age())tom.set_age(20 ) print (tom.age()) 输出: 18 20
通过age
和set_age
方法操作属性。
有没有简单的方式呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Person : def __init__ (self, name, age=18 ): self.name = name self.__age = age @property def age (self ): return self.__age @age.setter def age (self, age ): self.__age = age @age.deleter def age (self ): print ('del' ) tom = Person('Tom' ) print (tom.age)tom.age = 20 print (tom.age)del tom.age 输出: 18 20 del
特别注意:使用property
装饰器的时候这三个方法同名
property
装饰器
后面跟的函数名就是以后的属性名,属性名指的就是方法名称。它就是getter
。这个必须有,有了它至少是只读属性
setter
装饰器
与属性名同名,且接收2个参数,第一个是self
,第二个是将要赋值的值。有了它,属性可写
deleter
装饰器
可以控制是否删除属性。很少用。
property
装饰器必须在前,setter
、deleter
装饰器在后。
property
装饰器能通过简单的方式,把对方法的操作变成对属性的访问,并起到了一定隐藏效果
其它的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Person : def __init__ (self, name, age=18 ): self.name = name self.__age = age def getage (self ): return self.__age def setage (self, age ): self.__age = age def delage (self ): print ('del' ) age = property (getage, setage, delage, 'age property' ) tom = Person('Tom' ) print (tom.age)tom.age = 20 print (tom.age)del tom.age输出: 18 20 del
还可以如下
1 2 3 4 5 6 7 8 9 class Person : def __init__ (self, name, age=18 ): self.name = name self.__age = age age = property (lambda self:self.__age) tom = Person('Tom' ) print (tom.age)
对象的销毁 类中可以定义__del__
方法,称为析构函数(方法)。
作用:销毁类的实例的时候调用,以释放占用的资源。其中就放些清理资源的代码,比如释放连接。
注意这个方法不能引起对象的真正销毁,只是对象销毁的时候会自动调用它。
使用del
语句删除实例,引用计数减1。当引用计数为0时,会自动调用__del__
方法。
由于Python实现了垃圾回收机制,不能确定对象何时执行垃圾回收。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import timeclass Person : def __init__ (self, name, age=18 ): self.name = name self.__age = age def __del__ (self ): print ('delete {}' .format (self.name)) def test (): tom = Person('tom' ) tom.__del__() tom.__del__() tom.__del__() tom.__del__() print ('=========start=========' ) tom2 = tom tom3 = tom2 print (1 , 'del' ) del tom time.sleep(3 ) print (2 , 'del' ) del tom2 time.sleep(3 ) print ('~~~~~~~~~~~~~~~~~~' ) del tom3 time.sleep(3 ) print ('================end' ) test()
由于垃圾回收对象销毁时,才会真正清理对象,还会在之前自动调用__del__
方法,除非你明确知道自己的目的,建议不要手动调用这个方法。
方法重载(overload) 在其他面向对象的高级语言中,都有重载的概念。
所谓重载,就是同一个方法名,但是参数数量、类型不一样,就是同一个方法的重载。
Python没有重载!
Python不需要重载!
Python中,方法(函数)定义中,形参非常灵活,不需要指定类型(就算指定了也只是一个说明而非约束),参数个数也不固定(可变参数)。一个函数的定义可以实现很多种不同形式实参的调用。所以Python不需要方法重载。
或者说Python本身就实现了其它语言的重载。
封装 面向对象的三要素之一,封装Encapsulation
封装
将数据和操作组织到类中,即属性和方法
将数据隐藏起来,给使用者提供操作(方法)。使用者通过操作就可以获取或者修改数据。
getter
和setter
。
通过访问控制,暴露适当的数据和操作给用户,该隐藏的隐藏起来,例如保护成员或私有成员。
练习
随机整数生成类
可以指定一批生成的个数,可以指定数值的范围,可以调整每批生成数字的个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import randomclass RandomGen : """ 1. 解决随机数边界问题 2. 随机数生成 """ def __init__ (self, start=1 , stop=100 , count=10 ): self.start = start self.stop = stop self.count = count def generate (self, start=1 , stop=100 , count=10 ): return [random.randint(start, stop) for x in range (count)] class RandomGen : @classmethod def generate (cls, start=1 , stop=100 , count=10 ): return [random.randint(start, stop) for x in range (count)]
随机整数生成类,可以指定一批生成的个数,可以指定数值的范围,可以调整每批生成数字的个数。
使用生成器实现,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 import randomclass RandomGenerator : def __init__ (self, count=10 , start=1 , stop=100 ): self.count = count self.start = start self.stop = stop def generate (self ): for _ in range (self.count): yield random.randint(self.start, self.stop) rg = RandomGenerator() gen = rg.generate() print (next (gen)) ==================================================================================== import randomclass RandomGenerator : def __init__ (self, count=10 , start=1 , stop=100 ): self.count = count self.start = start self.stop = stop self._gen = self._generate() def _generate (self ): while True : yield random.randint(self.start, self.stop) def generate (self ): return [next (self._gen) for _ in range (self.count)] rg = RandomGenerator(20 ,1 ,1000 ) print (rg.generate())==================================================================================== import randomclass RandomGenerator : def __init__ (self, count=10 , start=1 , stop=100 ): self.count = count self.start = start self.stop = stop self._gen = self._generate() def _generate (self ): while True : yield [random.randint(self.start, self.stop) for _ in range (self.count)] def generate (self,count ): self.count = count return next (self._gen) rg = RandomGenerator() print (rg.generate(5 ))==================================================================================== import randomclass RandomGenerator : def __init__ (self, start=1 , stop=100 , patch=10 ): self.start = start self.stop = stop self.patch = patch self._gen = self._generate() def _generate (self ): while True : yield random.randint(self.start, self.stop) def generate (self, count=0 ): if count <= 0 : return [next (self._gen) for _ in range (self.patch)] else : return [next (self._gen) for _ in range (count)] a = RandomGenerator() print (a.generate())print (a.generate(5 ))import randomclass RandomGenerator : def __init__ (self, start=1 , stop=100 , patch=10 ): self.start = start self.stop = stop self.patch = patch self._gen = self._generate() def _generate (self ): while True : yield [random.randint(self.start, self.stop) for _ in range (self.patch)] def generate (self, count=0 ): if count > 0 : self.patch = count return next (self._gen) a = RandomGenerator() print (a.generate())print (a.generate(5 ))import randomclass RandomGenerator : def __init__ (self, start=1 , stop=100 , patch=10 ): self.start = start self.stop = stop self.patch = patch self._gen = self._generate() def _generate (self ): while True : yield [random.randint(self.start, self.stop) for _ in range (self.patch)] def generate (self ): return next (self._gen) @property def patch (self ): return self._patch @patch.setter def patch (self, value ): self._patch = value @property def _patch (self ): return self.patch @_patch.setter def _patch (self, value ): self.patch = value a = RandomGenerator() print (a.generate())a.patch = 5 print (a.generate())
打印坐标
使用上题中的类,随机生成20个数字,两两配对形成二维坐标系的坐标,把这些坐标组织起来,并打印输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Point : def __init__ (self, x, y ): self.x = x self.y = y def __str__ (self ): return '{}:{}' .format (self.x, self.y) def __repr__ (self ): retrun '{}:{}' .format (self.x, self.y) lst1 = [Point(x,y) for x,y in zip (rg.generate(10 ), rg.generate(10 ))] print (lst1)for p in lst1: print (p.x, p.y) ==================================================================================== class Point : def __init__ (self, x, y ): self.x = x self.y = y points = [Point(x,y) for x,y in zip (RandomGenerator(10 ).generate(),RandomGenerator(10 ).generate())] for p in points: print ('{}:{}' .format (p.x, p.y))
车辆信息
记录车的品牌mark、颜色color、价格price、速度speed等特征,并实现增加车辆信息、显示全部车辆信息的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class Car : def __init__ (self, mark, speed, color, price ): self.mark = mark self.speed = speed self.color = color self.price = price class CarInfo : def __init__ (self ): self.lst = [] def addcar (self, car:Car ): self.lst.append(car) def getall (self ): return self.lst ci = CarInfo() car = Car('audi' , 400 , 'red' , 100 ) ci.addcar(car) ci.getall() ==================================================================================== class Car : def __init__ (self, mark, speed, color, price ): self.mark = mark self.speed = speed self.color = color self.price = price class CarInfo : def __init__ (self ): self.info = [] def addcar (self, car:Car ): self.info.append(car) def getall (self ): return self.info ci = CarInfo() car = Car('audi' , 400 , 'red' , 100 ) ci.addcar(car) ci.getall()
实现温度的处理
思路
假定一般情况下,使用摄氏度为单位,传入温度值。
如果不给定摄氏度,一定会把温度值转换到摄氏度。
温度转换方法可以使用实例的方法,也可以使用类方法,使用类方法的原因是,为了不创建对象,就可以直接进行温度转换计算,这个类设计像个温度工具类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 class Temperature : def __init__ (self, t, unit='c' ): self._c = None self._f = None self._k = None if unit == 'k' : pass elif unit == 'f' : pass else : self._c = t @property def c (self ): return self._c @property def k (self ): pass @property def f (self ): pass @classmethod def c2f (cls, c ): return 9 *c/5 + 32 @classmethod def f2c (cls, f ): return 5 *(f-32 )/9 @classmethod def c2k (cls, c ): return c + 273.15 @classmethod def k2c (cls, k ): return k - 273.15 @classmethod def f2k (cls, f ): return cls.c2k(cls.f2c(f)) @classmethod def k2f (cls, k ): return cls.c2f(cls.k2c(k))
进一步完善未完成代码,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class Temperature : def __init__ (self, t, unit='c' ): self._c = None self._f = None self._k = None if unit == 'k' : self._k = t self._c = self.k2c(t) elif unit == 'f' : self._f = t self._c = self.f2c(t) else : self._c = t @property def c (self ): return self._c @property def k (self ): if self._k is None : self._k = self.c2k(self._c) return self._k @property def f (self ): if self._f is None : self._f = self.c2f(self._c) return self._f @classmethod def c2f (cls, c ): return 9 *c/5 + 32 @classmethod def f2c (cls, f ): return 5 *(f-32 )/9 @classmethod def c2k (cls, c ): return c + 273.15 @classmethod def k2c (cls, k ): return k - 273.15 @classmethod def f2k (cls, f ): return cls.c2k(cls.f2c(f)) @classmethod def k2f (cls, k ): return cls.c2f(cls.k2c(k)) print (Temperature.c2f(40 ))print (Temperature.c2k(40 ))print (Temperature.f2c(104.0 ))print (Temperature.k2c(313.15 ))print (Temperature.k2f(313.15 ))print (Temperature.f2k(104 ))t = Temperature(37 ) print (t.c, t.k, t.f)t = Temperature(300 , 'k' ) print (t.c, t.k, t.f)
模拟购物车购物
思路
购物车购物,分解得到两个对象购物车
、物品
,一个操作购买
。
购买不是购物车的行为,其实是人的行为,但是对于购物车来说就是增加add
。
商品有很多种类,商品的属性多种多样,怎么解决?
购物车可以加入很多不同的商品,如何实现?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Color : RED = 0 BLUE = 1 GREEN = 2 GOLDEN = 3 BLACK = 4 OTHER =1000 class Item : def __init__ (self, **kwargs ): self.__spec = kwargs def __repr__ (self ): return str (sorted (self.__spec.items())) class Cart : def __init__ (self ): self.items = [] def additem (self, item:Item ): self.items.append(item) def getallitems (self ): return self.items mycart = Cart() myphone = Item(mark='Huawei' , color=Color.GOLDEN, memory='4G' ) mycart.additem(myphone) mycar = Item(mark='Red Flag' , color=Color.BLACK, year=2017 ) mycart.additem(mycar) print (mycart.getallitems())
注意,以上代码只是一个非常简单的一个实现,生产环境实现购物车的增删改查,要考虑很多。
笔记 一切皆对象,如果都是对象,拿到的都是内存地址,都是引用,创建多个abc,内存中只会有一个abc,其他都是引用。创建列表时都会再次创建新列表,不要把列表放在函数中,函数多次调用会创建多个列表,如果只想要一份的话,就把列表放到函数外面
抽象是数据与动作的集合,对计算机来说数据称为属性,动作称为方法,有些地方把方法也叫函数。对象是抽象出来为实体服务的,如果拿出来对应就只能对应群体中的一个个体,这个个体就具有你所说的属性和方法,但属性可能是空白,因为虽然具体化了,但还没有把数据传给它。属性就是状态,状态就是数据,它是对象状态的抽象,因为属性最后放在类里面,而类是个抽象概念,所以属性也是个抽象概念。保存属性用数据结构。操作是个动作,这就是方法,实际上就是函数
写类时先想清楚有几个对象,然后把最核心的动作和数据抽象出来就行了。类定义完成后,这个类对象就产生了,类本身也是对象,这里区分一下类对象和类的对象,类对象指这是一个类,这个类本身也是个对象;类的对象指的是类的实例,实体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 class MyClass : pass class MyClass : """A example class""" x = 'abc' def foo (self ): return "MyClass foo" print (MyClass.x)print (MyClass.foo)print (MyClass.__doc__)例: class MyClass : """this is a class""" x = 123 def foo (self ): print (id (self)) return self print (MyClass)print (MyClass.__name__)print (MyClass.x) print (MyClass.foo) print (MyClass.__doc__) print (type (MyClass)) 输出: <class '__main__.MyClass' > MyClass 123 <function MyClass.foo at 0x7f8e81eec550 > this is a class <class 'type' > x是MyClass的变量,foo也可以是MyClass的变量,类的变量和类的属性指的是一个意思,所以x和foo也是MyClass的属性,变量才能绑定到方法上。实际都是属于类的标识符,如果不属于类,就不能用类来调用它们、访问它们 类里面的方法不能叫function,在Python中叫method print ('下面就是实例的东西了' )a = MyClass() print (a.foo())print (a.x) print (a.foo)print ('==============' )print (id (a))输出: 下面就是实例的东西了 139819469043120 123 None 123 <bound method MyClass.foo of <__main__.MyClass object at 0x7fd39a48c5b0 >> ============== 139819469043120 <__main__.MyClass object at 0x7fd6fd155a90 > *** 方法是属于类的属性,但是类的实例是对象,对象可以调用这个方法。常用的方法如下 print (MyClass.x)a = MyClass() print (a.foo())print (a.x) *** class MyClass : """this is a class""" x = 123 def __init__ (self ): print ('init' ) def foo (self ): return "foo = {}" .format (self.x) a = MyClass() print (a.foo())输出: init foo = 123 **** __init__(self)方法指的就是构造器或构造方法或初始化方法,初始化的发生时机在类的构建完发生,发生之后就不会再初始化了,也不会再构建了,除非再新建一个对象出来。 **** class Person : def __init__ (self ): self.name = 'tom' a = Person b = Person print (a.name, b.name)输出: tom tom ================================================================== class Person : x = 'abc' def __init__ (self, name ): self.name = name a = Person('tom' ) b = Person('jerry' ) print (a.name, b.name)print (a.x, b.x) 输出: tom jerry abc abc ==================================================================== class Person : x = 'abc' def __init__ (self, name ): self.name = name a = Person('tom' ) b = Person('tom' ) print (a == b) print (a is b)输出: False False ==================================================================== class Person : x = 'abc' def __init__ (self, name, age=18 ): self.name = name self.y = age def show (self, x, y ): print (self.name, self.y) self.y = x Person.x = x a = Person('tom' ) b = Person('tom' , 20 ) print (a.y, b.y)a.show(100 , 'a' ) b.show(200 , 'b' ) print (Person.x)a.y = 200 输出: 18 18 tom 18 100 a tom 20 200 b 100
实例叫instance
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 class Person : x = 'abc' def __init__ (self, name, age=18 ): self.name = name self.y = age def show (self, x, y ): print (self.name, self.y) self.y = x Person.x = x a = Person('tom' ) b = Person('jerry' , 20 ) print (a.__class__, b.__class__)print (a.__class__.__qualname__, a.__class__.__name__) print (isinstance (a, a.__class__)) print (int .__class__) print (isinstance (b, int .__class__))print (Person.__dict__) print (a.__dict__) print (b.__dict__) print (a.__dict__['name' ])输出: <class '__main__.Person' > <class '__main__.Person' > Person Person True <class 'type' > False {'__module__' : '__main__' , 'x' : 'abc' , '__init__' : <function Person.__init__ at 0x7fdc1d2d3550 >, 'show' : <function Person.show at 0x7fdc1d2d35e0 >, '__dict__' : <attribute '__dict__' of 'Person' objects>, '__weakref__' : <attribute '__weakref__' of 'Person' objects>, '__doc__' : None } {'name' : 'tom' , 'y' : 18 } {'name' : 'jerry' , 'y' : 20 } tom ================================================================ class Person : age = 3 height = 170 def __init__ (self, name, age=18 ): self.name = name self.age = age tom = Person('tom' ) jerry = Person('jerry' , 20 ) Person.age = 30 print (Person.age, tom.age, jerry.age)print (Person.__dict__, tom.__dict__, jerry.__dict__, sep='\n' )print (Person.height, tom.height, jerry.height)Person.height += 20 print (Person.height, tom.height, jerry.height)print (Person.__dict__, tom.__dict__, jerry.__dict__, sep='\n' )tom.height = 168 print (Person.height, tom.height, jerry.height)print (Person.__dict__, tom.__dict__, jerry.__dict__, sep='\n' )jerry.height += 30 print (Person.height, tom.height, jerry.height)print (Person.__dict__, tom.__dict__, jerry.__dict__, sep='\n' )Person.weight = 70 print (Person.weight, tom.weight, jerry.weight)print (Person.__dict__['weight' ]) print (tom.__dict__['weight' ]) pritn(tom.weight) 输出: 30 18 20 170 170 170 190 190 190 190 168 190 190 168 220
装饰一个类,这和类装饰器不同,类装饰器是指一个类就是装饰别人的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 def setnameproperty (name ): def wrapper (cls ): cls.NAME = name return cls return wrapper @setnameproperty('MY CLASS' ) class MyClass : pass print (MyClass.__dict__)输出: {'__module__' : '__main__' , '__dict__' : <attribute '__dict__' of 'MyClass' objects>, '__weakref__' : <attribute '__weakref__' of 'MyClass' objects>, '__doc__' : None , 'NAME' : 'MY CLASS' }
类方法与静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 class MyClass : """this is a class""" x = 123 def __init__ (self ): print ('init' ) def foo (self ): return "foo = {}" .format (self.x) def bar (): print ('bar' ) a = MyClass() print (a.foo())MyClass.bar() print (MyClass.__dict__)a.bar() 输出: init foo = 123 bar {'__module__' : '__main__' , '__doc__' : 'this is a class' , 'x' : 123 , '__init__' : <function MyClass.__init__ at 0x7fc1540c7550 >, 'foo' : <function MyClass.foo at 0x7fc1540c75e0 >, 'bar' : <function MyClass.bar at 0x7fc1540c7670 >, '__dict__' : <attribute '__dict__' of 'MyClass' objects>, '__weakref__' : <attribute '__weakref__' of 'MyClass' objects>} ============================================================= class MyClass : """this is a class""" xxx = 'XXX' def foo (self ): print ("foo" ) def bar (): print ('bar' ) @classmethod def clsmtd (cls, *, a ): print ('{}.xxx={}' .format (cls.__name__, cls.xxx)) @staticmethod def staticmtd (x ): print ('static' ) MyClass a = MyClass() a.foo() MyClass.bar() print (MyClass.__dict__)MyClass.clsmtd() a.clsmtd() MyClass.staticmtd() a.staticmtd() MyClass.clsmtd(a=MyClass()) MyClass().clsmtd() 输出: foo bar {'__module__' : '__main__' , '__doc__' : 'this is a class' , 'xxx' : 'XXX' , 'foo' : <function MyClass.foo at 0x7f10e5f7e550 >, 'bar' : <function MyClass.bar at 0x7f10e5f7e5e0 >, 'clsmtd' : <classmethod object at 0x7f10e5fe1a90 >, '__dict__' : <attribute '__dict__' of 'MyClass' objects>, '__weakref__' : <attribute '__weakref__' of 'MyClass' objects>} MyClass.xxx=XXX MyClass.xxx=XXX static static
访问控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Person : def __init__ (self, name, age=18 ): self.name = name self.__age = age def growup (self, incr=1 ): if 0 < incr < 150 : self.__aget += incr def getage (self ): return self.__age tom = Person('tom' ) tom.growup(2 ) print (tom.getage()) print (Person.__dict__)print (tom.__dict__)tom._Person__age = 200 print (tom.getage())tom.age = 300 print (tom.getage())print (tom.age)print (tom.__dict__)输出: 20 {'__module__' : '__main__' , '__init__' : <function Person.__init__ at 0x7f88d8859550 >, 'growup' : <function Person.growup at 0x7f88d88595e0 >, 'getage' : <function Person.getage at 0x7f88d8859670 >, '__dict__' : <attribute '__dict__' of 'Person' objects>, '__weakref__' : <attribute '__weakref__' of 'Person' objects>, '__doc__' : None } {'name' : 'tom' , '_Person__age' : 20 } 200
保护变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Person : def __init__ (self, name, age=18 ): self._name = name self.__age = age def __growup (self, incr=1 ): if 0 < incr < 150 : self.__aget += incr def getage (self ): return self.__age tom = Person('tom' ) print (Person.__dict__) print (tom.getage()) tom._name = 'jerry' print (tom._name)print (tom.__dict__)输出: {'__module__' : '__main__' , '__init__' : <function Person.__init__ at 0x7fbb17fca550 >, '_Person__growup' : <function Person.__growup at 0x7fbb17fca5e0 >, 'getage' : <function Person.getage at 0x7fbb17fca670 >, '__dict__' : <attribute '__dict__' of 'Person' objects>, '__weakref__' : <attribute '__weakref__' of 'Person' objects>, '__doc__' : None } 18 jerry {'_name' : 'jerry' , '_Person__age' : 20 }
猴子补丁
解释器运行时才会执行动态替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Person : def __init__ (self, chinese, english, history ): self. chinese = chinese self.eng = english self.his = history def getscore (self ): return (self.chinese, self.eng, self.his) def getscrore (self ): return dict (chi=self.chinese, eng=self.eng, his=self.his) from test3 import getscrore from test2 import Persondef monkeypatch4Person (): Person.getscore = getscrore student1 = Person(80 , 90 , 88 ) print (student1.getscore())monkeypatch4Person() student2 = Person(88 , 99 , 100 ) print (student2.getscore())输出: (80 , 90 , 88 ) ('his' : 88 , 'chi' : 80 , 'eng' : 90 )
属性装饰器与对象的销毁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 class Person : def __init__ (self, chinese, english, history ): self._chinese = chinese self._eng = english self.__his = history def gethis (self ): return self.__his def sethis (self, value ): self.__his = value def seteng (self, v ): self._eng = v @property def chinese (self ): return self._chinese @chinese.setter def chinese (self, value ): self._chinese = value @chinese.deleter def chinese (self ): print ('del chinese' ) eng = property (lambda self:self._eng, seteng) student1 = Person(80 ,90 ,88 ) print (student1.gethis()) print (student1.chinese) student1.chinese = 100 print (student1.chinese)del student1.chineseprint (student1.eng) student1.eng = 110