python-19 面向对象
面向对象
语言的分类
面向机器:
抽象成机器指令,机器容易理解
代表:汇编语言
面向过程:
做一件事情,排出个步骤,第一步干什么,第二步干什么,如果出现情况A,做什么处理,如果出现了情况B,做什么处理。
问题规模小,可以步骤化,按部就班处理
面向对象OOP:
随着计算机需要解决的问题的规模扩大,情况越来越复杂。需要很多人、很多部门协作,面向过程编程不太适合了
代表:C++、 Java、 Python等
什么是面向对象呢?
一种认识世界、分析世界的方法论。将完事万物抽象为类。
类class:
类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合。用计算机语言来描述类,就是属性和方法的集合。
对象instance、object:
对象是类的具象,是一个实体。
对于我们每个人这个个体,都是抽象概念人类的不同的实体。
你吃鱼
你,就是对象;鱼,也是对象;就就是动作
你是具体的人,是具体的对象。你属于人类,人类是个抽象的概念,是无数具体的个体的抽象。鱼,也是具体对象,就是你吃的这一条具体的鱼。这条鱼属于鱼类,是无数的鱼抽象出来的概念。
吃,是动作,也是操作,也是方法,这个吃是你的动作,也就是人类具有的方法。如果反过来,鱼吃人。吃就是鱼类的动作了。
吃,这个动作,很多动物都具有的动作,人类和鱼类都属于动物类,而动物类是抽象的概念,是动物都有吃的动作,但是吃法不容而已。
你驾驶车,这个车也是车类的具体的对象(实例),驾驶这个动作是鱼类不具有的,是人类具有的方法
属性,它是对象状态的抽象,用数据结构来描述。
操作,它是对象行为的抽象,用操作名和实现该操作的方法来描述。
哲学:一切皆对象,对象时数据和操作的封装,对象是独立的,但是对象之间可以相互作用,目前OOP是最接近人类认知的编程范式。
面向对象三要素
封装
组装:将数据和操作组装到一起
隐藏数据:对外只暴露一些接口,通过接口访问对象。比如驾驶员使用汽车,不需要了解汽车的构造细节,只需要知道使用什么部件怎么驾驶就行,踩了油门就能跑,可以不了解后面的机动原理。
继承
多复用,继承来的就不用自己写了
多继承少修改,OCP(Open-closed Principle),使用继承来改变,来体现个性
多态
面向对象编程最灵活的地方,动态绑定
人类就是封装;
人类继承自动物类,孩子继承父母特征。分为单一继承、多继承;
多态,继承自动物类的人类、猫类的操作“吃”不同。
Python的类
定义
class ClassName:
语句块
- 必须使用class关键字
- 类名必须是用大驼峰命名
- 类定义完成后,就产生了一个类型对象,绑定到了ClassName上
class MyClass:
"""A example class"""
x = 'abc' # 类属性
def foo(self): # 类属性foo, 也是方法
return 'MyClass'
print(MyClass) # <class '__main__.MyClass'>
print(MyClass.__name__) # MyClass
print(MyClass.x) # abc
print(MyClass.foo) # <function MyClass.foo at 0x0000026297261C18>
print(MyClass.__doc__) # A example class
print(type(MyClass)) # <class 'type'> 类类型对象
类对象及类属性
类对象 ,类的定义就会生成一个类对象
类的属性 ,类定义中的变量和类中定义的方法都是类的属性
类变量 ,x 是类MyClass的变量
MyClass中,x 、foo都是类的属性,__doc__也是类的属性;
foo方法是类的属性,如同吃是人类的方法,但是每一个具体的人才能吃东西,也就是说吃是人的实例才能调用的方法;
foo是 method方法对象 ,不是普通的函数对象function,它必须至少有一个参数,且第一个参数必须是 self(self可以换个名字),这个参数位置就留给了self;
self 自带当前实例本身
class MyClass:
"""A example class"""
x = 'abc' # 类属性
def foo(self): # 类属性foo, 也是方法
print(id(self))
return self
print(MyClass.x) # abc
a = MyClass() #实例化,初始化
print(a.x) # abc
print(a.foo) # <bound method MyClass.foo of <__main__.MyClass object at 0x00000296D84FE8C8>>
实例化
a = MyClass() #实例化,初始化
使用上面的语法,在类对象名称后面加上一个括号,就调用类的实例化方法,完成实例化。实例化就真正创建一个该类的对象(实例)。例如人类的实例tom、jerry。
实例化后获得的实例,是不同的实例,即使是使用同样的参数实例化,也得到了不一样的对象。Python类实例化后,会自动调用__int__方法。这个方法第一个参数必须留给self,其他参数随意。
__int__方法
MyClass()实际上调用的是__int__(self)方法,可以不定义,如果没有定义会在实例化后隐式调用。
作用:对实例进行 初始化
class MyClass:
def __int__(self):
print('init)
a = MyClass() #会调用__init__
初始化函数可以多个参数,请注意第一个位置必须是self,例如init(self,name,age)
class Person:
x = 'abc'
def __init__(self, name ):
self.name = name # 实例的属性 类对象的属性
a = Person('tom')
b = Person('tom')
print(a.name, a.x)
print(b.name, b.x)
print(a == b) # False
print(a is b) # False
class Person:
x = 'abc'
def __init__(self, name ,age=18):
self.name = name
self.age = age
def show(self):
print(self.name, self.age)
a = Person('tom')
b = Person('jerry','20')
print(a.name, a.x) # tom abc
print(b.name, b.x) # jerry abc
a.show() # tom 18
b.show() # jerry 20
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.x, x, y)
self.y = x
Person.x = x
a = Person('tom')
b = Person('jerry','20')
print(a.y, b.y) # 18 20
a.show(100,'a') # tom 18 abc 100 a
print(a.y) # 100
print(Person.x) # 100
注意:__init__()方法不能有换回值,也就是只能是None
实例对象instance
类实例化后一定会获得一个对象,就是实例对象 。上例中的tom、jerry就是Person类的实例。
__init__()方法的第一参数self就是指代某一个实例。
类实例化出一个实例对象,实例对象会绑定方法,调用方法时采用jerry.showage()的方式。
但是定义时showage(self),少传一个参数吗?
这个self就是jerry,Python会把方法的调用者作为第一参数self的实参传入。
self.name就是jerry对象的name,name是保存在了jerry对象上,而不是Persion类上。所以,称为实例变量 。
self
class MyClass:
def __init__(self):
print('self in init = {}'.format(id(self)))
c = MyClass() #会调用__init__
print('c = {}'.format(id(c))) # self in init = 2917925635400
# c = 2917925635400
d = MyClass()
print('d = {}'.format(id(d))) # self in init = 2917928265608
# d = 2917928265608
上例说明,self就是调用者,就是对应的实例对象。
self这个名字只是一个惯例,它可以修改,但是请不要修改,否则影响代码的可读性
实例变量和类变量
class Person:
age = 18 # 类变量
def __init__(self, name ):
self.name = name # 实例变量
实例变量是每一个实例自己的变量,是自己独有的;类变量是类的变量,是类的所有实例共享的属性和方法
特殊属性 | 含义 |
---|---|
__name__ | 对象名 |
__class__ | 对象的类型 |
__dict__ | 对象的属性的字典 |
__qualname__ | 类的限定名 |
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.x, x, y)
self.y = x
Person.x = x
a = Person('tom')
b = Person('jerry','20')
print(a.__class__, b.__class__) # <class '__main__.Person'> <class '__main__.Person'>
print(a.__class__.__qualname__, a.__class__.__name__) # Person Person
print(isinstance(a,a.__class__)) # True
print(Person.__dict__) # {'__module__': '__main__', 'x': 'abc', '__init__': <function Person.__init__ at 0x00000213A6078F78>, 'show': <function Person.show at 0x00000213A6078CA8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
print(a.__dict__) # {'name': 'tom', 'y': 18}
print(b.__dict__) # {'name': 'jerry', 'y': '20'}
print(a.__dict__['name']) # tom
print(sorted(Person.__dict__.items()), end='\n\n') # [('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x0000022BF8BB8CA8>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('show', <function Person.show at 0x0000022BF8BFDF78>), ('x', 'abc')]
上例中,可以看到类属性保存在类的__dict__中,实例属性保存在实例的__dict__中,如果从实例访问类的属性,就需要借助__class__找到所属的类。
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) # 30 18 20
print(Person.__dict__, tom.__dict__, jerry.__dict__, sep='\n')
# {'__module__': '__main__', 'age': 30, 'height': 170, '__init__': <function Person.__init__ at 0x0000014CFA6B1C18>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
# {'name': 'tom', 'age': 18}
# {'name': 'jerry', 'age': '20'}
print(Person.height, tom.height, jerry.height) # 170 170 170
Person.height += 20
print(Person.height, tom.height, jerry.height) # 190 190 190
print(Person.__dict__, tom.__dict__, jerry.__dict__, sep='\n')
# {'__module__': '__main__', 'age': 30, 'height': 190, '__init__': <function Person.__init__ at 0x0000014CFA6B1C18>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
# {'name': 'tom', 'age': 18}
# {'name': 'jerry', 'age': '20'}
tom.height = 168
print(Person.__dict__, tom.__dict__, jerry.__dict__, sep='\n')
# {'__module__': '__main__', 'age': 30, 'height': 190, '__init__': <function Person.__init__ at 0x0000014CFA6B1C18>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
# {'name': 'tom', 'age': 18, 'height': 168}
# {'name': 'jerry', 'age': '20'}
print(Person.height, tom.height, jerry.height) # 190 168 190
jerry.height += 30
print(Person.__dict__, tom.__dict__, jerry.__dict__, sep='\n')
# {'__module__': '__main__', 'age': 30, 'height': 190, '__init__': <function Person.__init__ at 0x0000014CFA6B1C18>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
# {'name': 'tom', 'age': 18, 'height': 168}
# {'name': 'jerry', 'age': '20', 'height': 220}
print(Person.height, tom.height, jerry.height) # 190 168 220
Person.height = 180
print(Person.__dict__, tom.__dict__, jerry.__dict__, sep='\n')
# {'__module__': '__main__', 'age': 30, 'height': 180, '__init__': <function Person.__init__ at 0x0000014CFA6B1C18>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
# {'name': 'tom', 'age': 18, 'height': 168}
# {'name': 'jerry', 'age': '20', 'height': 220}
print(Person.height, tom.height, jerry.height) # 180 168 220
Person.weight = 70
print(Person.weight, tom.weight, jerry.weight) # 70 70 70
print(Person.__dict__['weight']) # 70
print(tom.__dict__['weight']) # KeyError: 'weight'
print(tom.weight) # # 70
是类的,也是这个类所有实例的,其实例都可以访问到;是实例的,就是这个实例自己的,通过类访问不到。
类变量是属于类的变量,这个类的所有实例可以共享这个变量。
实例可以动态的给自己增加一个属性。实例 __dict__[变量名] 和 实例.变量名 都可以访问到。
实例的同名变量会隐藏这类变量,或者说是覆盖了这个类变量。
实例属性的查找顺序
指的是实例使用 . 来访问属性,会先找自己的__dict__,如果没有,然后通过属性__class__找到自己的类,再去类的__dict__中找。
tom.__class__.__dict__ == Person.__dict__
注意:如果实例使用__dict__[变量名] 访问变量,将不会按照上面的查找顺序找变量了。
一般来说,类变量使用全大写来命名
装饰一个类
# 增加类变量
def setnameproperty(name):
cls.NAME = name
# 改进成装饰器
def setnameproperty(name):
def wrapper(cls):
cls.NAME = name
return cls
return wrapper
@setnameproperty('MY CLASS')
class MyClass:
pass
print(MyClass.__dict__)
之所以能装饰,本质上是为类对象动态的添加了一个属性,而MyClass这个标识符指向这个类对象。
类方法和静态方法
前面的例子中定义的__init__等方法,这些方法本身都是类的属性,第一个参数必须是self,而self必须指向一个对象,也就是类必须实例化之后,由实例来调用这个方法。
普通函数
class MyClass:
"""A example class"""
xxx = 'XXX'
def foo(self):
print('foo')
def bar():
print('bar')
a = MyClass()
a.foo() # foo
MyClass.bar() # bar
print(MyClass.__dict__)
# {'__module__': '__main__', '__doc__': 'A example class', 'foo': <function MyClass.foo at 0x00000214A2188F78>, 'bar': <function MyClass.bar at 0x00000214A2188CA8>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>}
# a.bar() # TypeError: bar() takes 0 positional arguments but 1 was given
类方法
class MyClass:
"""A example class"""
xxx = 'XXX'
def foo(self):
print('foo')
def bar():
print('bar')
@classmethod # 类方法
def clsmtd(cls):
print('{}.xxx={}'.format(cls.__name__, cls.xxx))
@staticmethod
def staticmtd():
print('static')
# @staticmethod
# def staticmtds(x):
# print('static',x )
a = MyClass()
a.foo() # foo
MyClass.bar() # bar
print(MyClass.__dict__)
# {'__module__': '__main__', '__doc__': 'A example class', 'xxx': 'XXX', 'foo': <function MyClass.foo at 0x000002D7036240D8>, 'bar': <function MyClass.bar at 0x000002D703624168>, 'clsmtd': <classmethod object at 0x000002D70361EB08>, 'staticmtd': <staticmethod object at 0x000002D70361EB48>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>}
MyClass.clsmtd() # MyClass.xxx=XXX
a.clsmtd() # MyClass.xxx=XXX # a.__class__.clsmtd()
MyClass.staticmtd() # static
a.staticmtd() # static
# MyClass.staticmtds(5) # static 5 也可以传参
1、在类定义中,使用 @classmethod 装饰器的方法
2、必须至少有一个参数,且第一个参数留给了cls,cls指代调用者即类对象自身
3、cls这个表示符可以是任意合法名称,但是为了易读,请不要修改
4、通过cls可以直接操作类的属性
注意:无法通过cls操作类的实例
静态方法
1、在类定义中,使用@staticmethod装饰器修饰的方法
2、调用时,不会隐式的传入参数
静态方法,只是表明这个方法属于这个名词空间。函数归在一起,方便组织管理。
方法的调用
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_method():
print(Person.HETGHT)
print('~~~~~类访问')
print(1, Person.normal_method())
print(2, Person.method()) # TypeError: method() missing 1 required positional argument: 'self'
print(3, Person.class_method())
print(4, Person.static_method())
print(Person.__dict__)
print('~~~~~实例访问')
print('tom-----')
tom = Person()
print(1, tom.normal_method())
print(2, tom.method()) # TypeError: normal_method() takes 0 positional arguments but 1 was given
print(3, tom.class_method())
print(4, tom.static_method())
print(5, tom.__class__.__dict__)
jerry = Person()
print(1, jerry.normal_method())
print(2, jerry.method()) # TypeError: normal_method() takes 0 positional arguments but 1 was given
print(3, jerry.class_method())
print(4, jerry.static_method())
上例输出什么???
normal_method() ,普通函数只能通过类访问
method(self),需生成实例后,通过实例访问
类几乎可以调用所有内部定义的方法,但是调用普通的方法时会报错,原因是第一参数必须是类的实例。
实例也几乎可以调用所有的方法,普通的函数的调用一般不可能出现,因为不允许这么定义。
总结:
类除了普通方法都可以调用,普通方法需要对象的实例作为第一参数。
实例可以调用所有类中定义的方法(包括类方法、静态方法),普通方法传入实例自身,静态方法和类方法需要找到实例的类。
访问控制
私有(Private)属性
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) # 38
p1.age = 160 # 超过了范围,并绕过了控制逻辑
print(p1.age) # 160
上例本来是想通过方法来控制属性,但是由于属性在外部可以访问,或者说可见,就可以直接绕过方法,直接修改这个属性。
Python提供了私有属性可以解决这个问题。
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def growup(self, incr=1):
if 0 < incr < 150 : # 控制逻辑
self.__age += incr
def getage(self):
return self.__age
tom = Person('tom')
tom.growup(2) # 正常的范围
print(tom.getage()) # 20
使用双下划线开头的属性名,就是私有属性
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def growup(self, incr=1):
if 0 < incr < 150 : # 控制逻辑
self.__age += incr
tom = Person('tom')
tom.growup(2) # 正常的范围
print(tom.__age) # AttributeError: 'Person' object has no attribute '__age'
通过实验可以看到,外部已经访问不到__age了,age 根本就没有定义,更是访问不到。
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def growup(self, incr=1):
if 0 < incr < 150 : # 控制逻辑
self.__age += incr
def getage(self):
return self.__age
tom = Person('tom')
tom.growup(2) # 正常的范围
print(tom.getage()) # 20
print(tom.__dict__) # {'name': 'tom', '_Person__age': 20}
tom._Person__age = 30
print(tom.getage()) # 30
print(tom.__dict__) # {'name': 'tom', '_Person__age': 30}
tom.__age = 35
print(tom.getage()) # 30
# 为什么年龄不一样?
print(tom.__dict__) # {'name': 'tom', '_Person__age': 30, '__age': 35}
私有变量的本质:
类定义的时候,如果声明一个实例变量的时候,使用双下划线,Python解释器会将其改名 ,转换名称为_类名__变量名 的名称,所以用原来的名字访问不到了。
知道了私有变量的新名称,就可以直接从外部访问到,并可以修改它。tom._Person__age = 30
保护变量
在变量名称前使用一个下划线,称为保护变量。
class Person:
def __init__(self, name, age=18):
self.name = name
self._age = age
tom = Person('tom')
print(tom._age) # 18
print(tom.__dict__) # {'name': 'tom', '_age': 18}
可以看出,这个_age属性根本就没有改变名称,和普通的属性一样,解释器不做任何特俗处理。这只是开发者共同的约定,看见这种变量,就如同私有变量,不要直接使用。
私有方法
参照保护变量、私有变量,使用单下划线、双下划线命名方法。
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()) # Tom
# print(tom.__getage()) # AttributeError: 'Person' object has no attribute '__getage'
print(tom.__dict__) # {'name': 'Tom', '_age': 18}
print(tom.__class__.__dict__) # {'__module__': '__main__', '__init__': <function Person.__init__ at 0x0000022BAD5A8CA8>, '_getname': <function Person._getname at 0x0000022BAD5EDF78>, '_Person__getage': <function Person.__getage at 0x0000022BAD5F4048>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
print(tom._Person__getage()) # 18
私有方法的本质
单下划线的方法只是开发这之间的约定,解释器不做任何改变。
双下划线的方法,是私有方法,解释器会改名,改名策略和私有变量相同,_类名__方法名 。
方法变量都在类的 __dict__ 中可以找到。
私有成员的总结
在Python中使用 _单下划线 或者 __双下划线 来标识一个成员被保护或者被私有化隐藏起来。
但是,不管使用什么样的访问控制,都不能正真的阻止用户修改类的成员。Python中没有绝对的安全的保护成员或私有成员。
因此,前导的下划线只是一种告警或者提醒,请遵守这个约定。除非真的有必要,不要修改或者使用保护成员或者私有成员,更不要修改它们。
补丁
可以通过修改或者替换类的成员。使用者调用的方式没有改变,但是,类提供的功能可能已经改变了。
猴子补丁(Monkey Patch):在运行时,对属性经行动态替换。黑魔法,慎用。
# test2.py
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)
-----------------------------------------------------------
# test3.py
def getscore(self):
return dict(chi=self.chinese, eng=self.eng, his=self.his)
-----------------------------------------------------------
# test1.py
from test2 import Person
from test3 import getscore
def monkeypatch4Person():
Person.getscore = getscore
student1 = Person(80,90,88)
print(student1.getscore()) # (80, 90, 88)
monkeypatch4Person()
student2 = Person(70,80,98)
print(student2.getscore()) # {'chi': 70, 'eng': 80, 'his': 98}
使用猴子补丁,替换了getscore方法。(从外部破坏封装,临时对付)
属性装饰器
一般好的设计是:把实例的属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性。
class Person:
def __init__(self, chinese, english, history):
self._chinese = chinese
self._eng = english
self.__his = history
def gethis(self): # getter
return self.__his
def sethis(self, value): # setter
self.__his = value
def geteng(self): # getter
return self._eng
def seteng(self, value):
self._eng = value
@property
def chinese(self):
return self._chinese
@chinese.setter
def chinese(self, value):
self._chinese = value
@chinese.deleter
def chinese(self):
# del self._chinese
print('del chinese')
eng = property(lambda self:self._eng, seteng)
student1 = Person(80,90,88)
print(student1.chinese) # 80
student1.chinese = 100
print(student1.chinese) # 100
del student1.chinese # del chinese
print(student1.eng) # 90
student1.eng = 110
print(student1.eng) # 110
property装饰器:后面跟的函数名就是以后的属性名。它就是getter。这个必须有,有了它至少是只读属性
setter装饰器:与属性名同名,且接受2个参数,第一个是self,第二个是将要赋值的值。有了它,属性可写
deleter装饰器:可以控制是否删除属性。很少用
property装饰器必须在前,setter、deleter装饰器在后
class C(object):
@property
def x(self):
"I am the 'x' property."
return self.\_x
@x.setter
def x(self, value):
self.\_x = value
@x.deleter
def x(self):
del self.\_x
# (copied from class doc)
property装饰器能通过简单的方式,把对方法的操作变成对属性的访问,并起到了一定隐藏效果
其他的写法
class Person:
def __init__(self, name, age=18):
self.name = name
self.__age = age
def setage(self, value):
self.__age = value
age = property(lambda self:self.__age + 2, setage)
tom = Person('Tom')
print(tom.age) # 20
tom.age = 22
print(tom.age) # 24
对象的销毁
类中可以定义__del__方法,称为析构函数(方法)
作用:销毁类的实例的时候调用,以释放占用的资源。
由于Python实现了垃圾回收机制,这个方法不能确定何时执行。有必要时,请使用 del语句删除实例,来手动调用这个方法。
class Person:
def __int__(self, name, age=18):
self.name = name
self.__age = age
def __del__(self):
print('delete {}'.format(self.name))
tom = Person('Tom')
del tom
方法重载(overload)
在其他面向对象的高级语言中,都有重载的概念。所谓重载,就是同一个方法名,但是参数数量、类型不一样,就是同一个方法的重载。
Python没有重载!
Python不需要重载!
Python中,方法(函数)定义中,形参非常灵活,不需要指定类型(就算指定了也只是一个说明而非约束),参数个数也不固定(可变参数)。一个函数的定义可以实现很多种不同形式参数的调用。所以Python不需要方法的重载。
封装
面向对象的三要素之一,封装Encapsulation
将数据和操作组织到类中,即属性和方法
将数据隐藏起来,给使用者提供操作。使用者通过操作就可以获取或者修改数据。getter和setter
通过访问控制,暴露适当的数据和操作给用户,该隐藏的隐藏起来。保护成员或私有成员
练习
1、随机整数生成类,可以指定一批生成的个数,可以指定数值的范围,可以调整每批生成数字的个数
基础版
import random
class randnum:
def __init__(self, min=0, max=100, num=10):
self.min = min
self.max = max
self.num = num
def create(self):
return [random.randint(self.min,self.max) for _ in range(self.num)]
rd = randnum()
print(rd.create()) # [26, 48, 0, 14, 0, 56, 0, 11, 70, 64]
---------------------------------------------------------------
import random
class randnum:
@classmethod
def create(self, min=0, max=100, num=10):
return [random.randint(min,max) for _ in range(num)]
rd = randnum()
print(rd.create()) # [66, 77, 75, 28, 86, 68, 6, 29, 79, 13]
进阶版
import random
class 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()
lst = rg.generate()
print(lst) # [99, 61, 11, 100, 28, 15, 86, 70, 57, 68]
--------------------------------------------------
import random
class 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()
lst = rg.generate(12)
print(lst) # [65, 51, 90, 88, 69, 50, 50, 16, 53, 40, 72, 93]
2、使用上题中的类,随机生成20个数字,两两配成对行成二维坐标系的坐标,把这些坐标组织起来,并打印输出
import random
class 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)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# def __repr__(self): # 按格式输出
# return '{}:{}'.format(self.x, self.y)
rg = RandomGenerator()
lst1 = [Point(x, y) for x, y in zip(rg.generate(10),rg.generate(10))]
print(lst1) # [<__main__.Point object at 0x000001FA818429C8>, ...]
for p in lst1:
print(p.x, p.y) # 27 44
3、记录车的品牌mark、颜色color、价格speed等特征,并实现增加测量信息、显示全部车辆信息的功能
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 getcar(self):
return self.lst
ci = CarInfo()
car = Car('audi', 400, 'red', 100)
ci.addcar(car)
4、实现温度的处理
实现华氏温度和摄氏温度的转换
°C = 5 * (°F - 32)/ 9
°F = 9 * °C / 5 + 32
增加与开氏温度的转换,K = °C + 273.15
class Temperature:
def __init__(self, c=None, f=None, k=None):
self.c = c
self.f = f
self.k = k
def getc(self):
return self.c
def getf(self):
return 9 * self.c / 5 + 32
def getk(self):
return self.getc() + 273.15
tp = Temperature(12)
print(tp.getc())
print(tp.getf())
print(tp.getk())
class Temparture:
def __init__(self, t, unit='c'):
self._c = None
self._f = None
self._k = None
if unit == 'f':
self._f = t
self._c = self.f2c(t)
elif unit == 'k':
self._k = t
self._c = self.k2c(t)
else:
self._c = t
@property
def c(self):
return self._c
@property
def f(self):
if self._f == None:
self._f = self.c2f(self._c)
return self._f
@property
def k(self):
if self._k == None:
self._k = self.c2k(self._c)
return self._k
@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(Temparture.c2f(40))
t = Temparture(37)
print(t.c, t.k, t.f)
t = Temparture(37, 'k')
print(t.c, t.k, t.f)
5、模拟购物车购物
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()
myhone = Item(mark='Huawei', color=Color.GOLDEN, memory='4G')
mycart.additem(myhone)
mycar = Item(mark='Red Flag', color=Color.BLACK, year=2017)
mycart.additem(mycar)
print(mycart.getallitems()) # [[('color', 3), ('mark', 'Huawei'), ('memory', '4G')], [('color', 4), ('mark', 'Red Flag'), ('year', 2017)]]