Python学习笔记(四)

1. 面向对象编程(OOP)

面向对象编程,简称OOP,是一种程序设计思想。OOP把对象做为程序的基本单元,一个对象包含了数据和操做数据的函数。
在Python中,全部数据类型均可以视为对象,固然也能够自定义对象。自定义的对象数据类型就是面向对象中的(Class)的概念。git

 
 
 
 
class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print('%s: %s' % (self.name, self.score))

给对象发消息其实是调用对象对应的关联函数,称之为对象的方法(Method)编程

1.1 类和实例

面向对象最重要的概念就是类(Class)和实例(Insance)。
在Python中定义类用class关键字,class后紧接着是类名,类名一般是大写开头的单词,紧接着是Object。
因为类能够起到模板的做用,所以,在建立实类的时候,把一些咱们认为是必须绑定的属性强制填写进去。经过定义一个特殊的_init_方法。api

 
 
 
 
class Student(object): def __init__(self, name, score): # 注意_init_第一个参数永远是self,表示建立实例的自己 self.name = name self.score = score

数据封装:
封装数据的函数是和类自己关联起来的,咱们称之为类的方法:app

 
 
 
 
class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): # 除了第一个参数是self外,其余和普通函数同样 print('%s: %s' % (self.name, self.score))

1.2 访问限制

若是让内部属性不被外部访问修改,能够把属性的名称的前面加上两个下划线_,在Python中,实例的变量名若是以__开头,就变成了一个私有变量(private),只有内部能够访问,外部不能访问。ssh

 
 
 
 
class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.__score))

若是外部代码想获取和修改name和score,能够给类增长get_score和set_score这样的方法。ide

 
 
 
 
class Student(object): ... def get_score(self): return self.__score def set_score(self, score): self.__score = score

在Python中,以双下划线_开头和以双下划线_结束的变量名,相似__xx__这样的是特殊变量,特殊变量是能够直接访问的,不是private变量。
以一个下划线开头的变量,好比_name,这样的实例变量是能够直接访问的,可是按照约定成俗的规定,意思就是”虽然我能够被直接访问,可是,请把我视为私有变量,不要随意访问”。
双下划线开头的变量,好比__name也不是必定不能被外部直接访问,能够经过_Student__name来访问__name变量。函数

1.3 继承和多态

在OOP程序设计中,当咱们定义一个Class的时候,能够从某个现有的Class继承,新的Class类称为子类,而被继承的类称为基类,父类或者超类。学习

 
 
 
 
# 基类class Animal(object): def run(self): print('Animal is running...')# 子类class Dog(Animal): pass

继承的好处:网站

  1. 子类能够继承父类全部的方法。
  2. 子类能够本身增长方法。ui

    当子类和父类同时拥有相同的方法时,子类覆盖了父类的方法,执行子类的方法,称之为多态
    多态的好处,就是著名的开闭原则。对拓展开放,对修改关闭。
    静态语言VS动态语言
    对于静态语言(Java)来讲,若是须要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,不然,则没法调用run()方法。
    对于动态语言(Python)来讲,则不必定须要传入Animal类,咱们只须要保证传入的对象一个run()方法就行。

 
 
 
 
class Timer(object): def run(self): print('Start...')

1.4 获取对象信息

  1. 判断对象类型,使用type()。
  2. 对于class继承关系来讲,使用type()就不方便,要判断class的类型,可使用isinstance()函数。能用type()判断的基本类型也能够用isinstance()判断。
  3. 若是要得到一个对象的全部属性和方法,可使用dir()函数,返回一个包含字符串的list。
 
 
 
 
# type()判断对象类型>>> type(abs)<class 'builtin_function_or_method'>>>> type(a)<class '__main__.Animal'># isinstance()判断对象类型>>> isinstance([1, 2, 3], (list, tuple))True>>> isinstance((1, 2, 3), (list, tuple))True# dir()获取对象的全部属性和方法>>> dir('ABC')['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

1.5 实例属性和类属性

因为Python是动态语言,根据类建立的实例能够绑定任意属性。
在编写程序的时候,千万不要把实例属性和类属性使用相同的名字,由于相同名称的实例属性将屏蔽掉类属性,当删除实例属性的时候,再使用相同的名称,访问到的将是类属性。

 
 
 
 
>>> class Student(object):... name = 'Student'...>>> s = Student() # 建立实例s>>> print(s.name) # 打印name属性,由于实例并无name属性,因此会继续查找class的name属性Student>>> print(Student.name) # 打印类的name属性Student>>> s.name = 'Michael' # 给实例绑定name属性>>> print(s.name) # 因为实例属性优先级比类属性高,所以,它会屏蔽掉类的name属性Michael>>> print(Student.name) # 可是类属性并未消失,用Student.name仍然能够访问Student>>> del s.name # 若是删除实例的name属性>>> print(s.name) # 再次调用s.name,因为实例的name属性没有找到,类的name属性就显示出来了Student

2. 面向对象高级编程

2.1 使用_slots_

为了限制实例的属性,Python容许在定义class的时候,定义一个特殊的_slots_变量。

 
 
 
 
class Student(object): __slots__ = ('name', 'age') # 用tuple定义容许绑定的属性名称
 
 
 
 
>>> s = Student() # 建立新的实例>>> s.name = 'Michael' # 绑定属性'name'>>> s.age = 25 # 绑定属性'age'>>> s.score = 99 # 绑定属性'score'Traceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: 'Student' object has no attribute 'score'

使用_slots_要注意,_slots_定义的属性仅对当前类实例起做用,对继承的子类是不起做用的。

2.2 使用@property

在绑定属性的时候,咱们不能直接把属性暴露出去,为了限制属性的范围,能够经过get和set方法检查参数。
Python内置的@property装饰器就是负责把一个方法变成属性调用。

 
 
 
 
class Student(object): @property def score(self): return self._score @score.setter # 只定义getter方法,不定义setter方法就代表该属性只读 def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value

2.3 多重继承

经过多重继承,一个子类就能够同时得到多个子类的全部功能。
在设计类的继承关系时,一般,主线是单一继承下来的,若是须要混入”额外”的功能,一般就须要多重继承来实现。这种设计称之为”MixIn”。
咱们不须要负责庞大的继承链,只有选择组合不一样类的功能,就能够快速构造出所需的子类。

2.4 定制类

_str_
print打印实例的时候,会出现一堆相似<main.Student object at 0x109afb190>,为了打印好看,咱们只须要定义一个_str_方法。可是直接打印的时候仍是会出现相似的问题,那么再定义一个_repr_()就会与_str_()效果同样。

 
 
 
 
class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name=%s)' % self.name __repr__ = __str__

_iter_
若是一个类想被用于for…in…循环,相似list和tuple这样,就必须实现一个_iter_方法,该方法返回一个迭代对象。

 
 
 
 
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例自己就是迭代对象,故返回本身 def __next__(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration(); return self.a # 返回下一个值
 
 
 
 
>>> for n in Fib():... print(n)...11235...4636875025

_getitem_
按照下标取出元素,实现切片的功能,就要实现_getitem_方法。

 
 
 
 
class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): # n是切片 start = n.start stop = n.stop if start is None: start = 0 a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L

_getattr_
正常状况下,当咱们调用类的方法或属性的时候,若是不存在就会报错,为了不这个错误,Python中的_getattr_()方法能动态返回一个属性。

 
 
 
 
class Student(object): def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr=='score': return 99

_call_
一个对象的实例能够有本身的属性和方法,当咱们调用实例方法时,咱们用instance.method()来调用,可是任何类只须要定义一个_call_()方法,就能够直接对实例进行调用。

 
 
 
 
class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name) --- >>> s = Student('Michael') >>> s() # self参数不要传入 My name is Michael.

经过callable()函数,咱们就能够断定一个对象是不是”可调用”对象。

2.5 使用枚举类

当咱们须要定义常量的时候,一个办法就是用大写常量名经过整数定义,更好的办法是为这样的枚举类型定义一个class类型,而后,每一个常量是class的惟一的实例。Python提供了Enum类来实现这个功能。

 
 
 
 
from enum import EnumMonth = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))# 这样咱们就得到了Month类型的枚举类,能够直接使用Month.Jan来引用一个变量,或者枚举它的全部成员。for name, member in Month.__members__.items(): print(name, '=>', member, ',', member.value)

若是须要更精确的控制枚举类型,能够从Enum派生出自定义类。

 
 
 
 
from enum import Enum, unique@unique # @unique装饰器能够帮助咱们检查保证没有重复值class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6 # 访问枚举类型的若干种方法 >>> day1 = Weekday.Mon>>> print(day1)Weekday.Mon>>> print(Weekday.Tue)Weekday.Tue>>> print(Weekday['Tue'])Weekday.Tue>>> print(Weekday.Tue.value)2>>> print(day1 == Weekday.Mon)True>>> print(day1 == Weekday.Tue)False>>> print(Weekday(1))Weekday.Mon>>> print(day1 == Weekday(1))True>>> Weekday(7)Traceback (most recent call last): ...ValueError: 7 is not a valid Weekday>>> for name, member in Weekday.__members__.items():... print(name, '=>', member)...Sun => Weekday.SunMon => Weekday.MonTue => Weekday.TueWed => Weekday.WedThu => Weekday.ThuFri => Weekday.FriSat => Weekday.Sat # 既能够根据成员名称引用枚举常量,又能够根据value的值得到枚举常量。

2.6 使用元类

class的定义是运行时动态建立的,而建立class的方法就是使用type()函数。
type()函数能够查看一个类的类型或变量的类型,既能够返回一个对象的类型,又能够建立出新的类型。

 
 
 
 
>>> def fn(self, name='world'): # 先定义函数... print('Hello, %s.' % name)...>>> Hello = type('Hello', (object,), dict(hello=fn)) # 建立Hello class>>> h = Hello()>>> h.hello()Hello, world.>>> print(type(Hello))<class 'type'>>>> print(type(h))<class '__main__.Hello'>

除了使用type()动态建立类之外,要控制类的建立行为,还可使用metaclass。
metaclass,直译为元类,简单的解释就是,当咱们定义了类以后,就能够根据类建立出实例,因此,先定义类,而后建立实例。
先定义metaclass,就能够建立类,最后建立实例。


感谢廖雪峰的官方网站提供的教程。Python学习笔记系列都基于廖老师的教程。



相关文章
相关标签/搜索