Python:笔记(3)——面向对象编程
类型与对象
术语
程序中存储的所有数据都是对象。每个对象都有一个身份、一个类别和一个值。
如:a=42,就是用值42创建了一个整数对象。
大多数对象都拥有大量特点的属性和方法。
- 属性就是与对象相关的值。
- 方法就是被调用时将在对象上执行某些操作的函数。
- 使用 . 运算符可以访问属性和方法。
Python中对象的一些说明
- 检查对象类型最佳的方式是利用内置函数 isinstance(object,type)
- 所有对象都有引用计数,当一个对象的引用计数归零时,他将会被垃圾收集机制处理掉。
- 对于像字符串和数字这样的不可变对象,a=b,实际上是创建了一个新的副本。
- 浅复制将创建一个新对象,但是它里面包含的值是对原始对象中包含的项的引用。
- 深复制将创建一个新对象,并且递归地复制它所包含的所有对象。可以使用copy.deepcopy()函数完成该工作。
- Python中的所有对象都是第一类的,也就是说能够命名的所有对象都可以当做数据处理。
演示浅层和深层复制
1 # 浅层复制 2 a=[1,2,[3,4]] 3 b=list(a) #创建a的一个浅复制 4 print(b is a) 5 b.append(100) 6 print(b) 7 print(a) 8 b[2][0]=-100 9 print(b) 10 print(a)
【结果】:
False
[1, 2, [3, 4], 100]
[1, 2, [3, 4]]
[1, 2, [-100, 4], 100]
[1, 2, [-100, 4]]
1 #深层复制 2 import copy 3 a=[1,2,[3,4]] 4 b=copy.deepcopy(a) 5 b[2][0] =-100 6 print(b) 7 print(a)
【结果】: [1, 2, [-100, 4]] [1, 2, [3, 4]]
类和面向对象编程
类的创建
1 对象初始化方法 2 class myClass(object): 3 def __init__(self,type): 4 self.type=type 5 def printInfo(self): 6 print(self.name,‘age is‘,self.age) 7 print(self.name,"age is",self.age, sep=‘,‘) 8 9 10 aClass = myClass(1) 11 # 给对象赋予属性 12 aClass.name = ‘Bob‘ 13 aClass.age=19 15 # 查看对象属性 16 aClass.name 17 aClass.age 18 aClass.type 19 # 调用对象方法 20 aClass.printInfo() 21 【输出】 22 Bob age is 19 23 Bob,age is,19
【几点说明】:
- 注意到
__init__
方法的第一个参数永远是self
,表示创建的实例本身,因此,在__init__
方法内部,就可以把各种属性绑定到self
,因为self
就指向创建的实例本身。 - 和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量
self
,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。 - 和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同。
访问限制
说明:
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线_,在Python中,实例的变量名如果以 _ 开头,就变成了一个私有变量(private),只有内部可以访问。
1 class Student(object): 2 def __init__(self, name, score): 3 self.__name = name 4 self.__score = score 5 6 def print_score(self): 7 print(‘%s: %s‘ % (self.__name, self.__score)) 8 def getName(self): 9 print(self.__name)
【说明】
bob = Student(‘Bob‘,99)
bob.print_score()
bob.getName()
bob.__name #这是不允许的
【几点说明】
- 在Python中,变量名类似
__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__
、__score__
这样的变量名。 - 有些时候,你会看到以一个下划线开头的实例变量名,比如
_name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
继承和多态
动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
Python允许使用多重继承,于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:
1 class Animal(object): 2 def run(self): 3 print(self.name) 4 def __init__(self,name): 5 self.name=name 6 class Dog(Animal): 7 pass 8 9 def test(animal): 10 animal.run() 11 12 dog = Dog("Animal") 13 test(dog)
获取对象信息
1.如果要获得一个对象的所有属性和方法,可以使用dir()
函数,它返回一个包含字符串的list.
2.配合getattr()
、setattr()
以及hasattr()
,我们可以直接操作一个对象的状态
3.总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”。
使用__slots__限制实例属性
Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class实例能添加的属性:
1 class Student(object): 2 __slots__ = (‘name‘, ‘age‘) # 用tuple定义允许绑定的属性名称 3 4 >>> s = Student() # 创建新的实例 5 >>> s.name = ‘Michael‘ # 绑定属性‘name‘ 6 >>> s.age = 25 # 绑定属性‘age‘ 7 >>> s.score = 99 # 绑定属性‘score‘ 8 Traceback (most recent call last): 9 File "<stdin>", line 1, in <module> 10 AttributeError: ‘Student‘ object has no attribute ‘score‘
使用@property
1.装饰器限制属性取值:把一个getter方法变成属性,只需要加上@property
就可以了,我们也可以添加setter方法
1 class Student(object): 2 @property 3 def score(self): 4 return self._score 5 @score.setter 6 def score(self, value): 7 if not isinstance(value, int): 8 raise ValueError(‘score must be an integer!‘) 9 if value < 0 or value > 100: 10 raise ValueError(‘score must between 0 ~ 100!‘) 11 self._score = value
2.设置只读属性:只定义getter方法,不定义setter方法就是一个只读属性
1 class Student(object): 2 3 @property 4 def birth(self): 5 return self._birth 6 7 @birth.setter 8 def birth(self, value): 9 self._birth = value 10 11 @property 12 def age(self): 13 return 2015 - self._birth
使用枚举类
1.简单创建枚举类
1 from enum import Enum 2 3 Month = Enum(‘Month‘, (‘Jan‘, ‘Feb‘, ‘Mar‘, ‘Apr‘, ‘May‘, ‘Jun‘, ‘Jul‘, ‘Aug‘, ‘Sep‘, ‘Oct‘, ‘Nov‘, ‘Dec‘))
2.继承Enum:@unique
装饰器可以帮助我们检查保证没有重复值。
1 from enum import Enum, unique 2 3 @unique 4 class Weekday(Enum): 5 Sun = 0 # Sun的value被设定为0 6 Mon = 1 7 Tue = 2 8 Wed = 3 9 Thu = 4 10 Fri = 5 11 Sat = 6