zoukankan      html  css  js  c++  java
  • Python学习笔记(四)

    1. 面向对象编程(OOP)

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

    1. class Student(object):
    2. def __init__(self, name, score):
    3. self.name = name
    4. self.score = score
    5. def print_score(self):
    6. print('%s: %s' % (self.name, self.score))

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

    1.1 类和实例

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

    1. class Student(object):
    2. def __init__(self, name, score): # 注意_init_第一个参数永远是self,表示创建实例的本身
    3. self.name = name
    4. self.score = score

    数据封装:
    封装数据的函数是和类本身关联起来的,我们称之为类的方法:

    1. class Student(object):
    2. def __init__(self, name, score):
    3. self.name = name
    4. self.score = score
    5. def print_score(self): # 除了第一个参数是self外,其他和普通函数一样
    6. print('%s: %s' % (self.name, self.score))

    1.2 访问限制

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

    1. class Student(object):
    2. def __init__(self, name, score):
    3. self.__name = name
    4. self.__score = score
    5. def print_score(self):
    6. print('%s: %s' % (self.__name, self.__score))

    如果外部代码想获取和修改name和score,可以给类增加get_score和set_score这样的方法。

    1. class Student(object):
    2. ...
    3. def get_score(self):
    4. return self.__score
    5. def set_score(self, score):
    6. self.__score = score

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

    1.3 继承和多态

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

    1. # 基类
    2. class Animal(object):
    3. def run(self):
    4. print('Animal is running...')
    5. # 子类
    6. class Dog(Animal):
    7. pass

    继承的好处:

    1. 子类可以继承父类所有的方法。
    2. 子类可以自己增加方法。

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

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

    1.4 获取对象信息

    1. 判断对象类型,使用type()。
    2. 对于class继承关系来说,使用type()就不方便,要判断class的类型,可以使用isinstance()函数。能用type()判断的基本类型也可以用isinstance()判断。
    3. 如果要获得一个对象的所有属性和方法,可以使用dir()函数,返回一个包含字符串的list。
    1. # type()判断对象类型
    2. >>> type(abs)
    3. <class 'builtin_function_or_method'>
    4. >>> type(a)
    5. <class '__main__.Animal'>
    6. # isinstance()判断对象类型
    7. >>> isinstance([1, 2, 3], (list, tuple))
    8. True
    9. >>> isinstance((1, 2, 3), (list, tuple))
    10. True
    11. # dir()获取对象的所有属性和方法
    12. >>> dir('ABC')
    13. ['__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是动态语言,根据类创建的实例可以绑定任意属性。
    在编写程序的时候,千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,当删除实例属性的时候,再使用相同的名称,访问到的将是类属性。

    1. >>> class Student(object):
    2. ... name = 'Student'
    3. ...
    4. >>> s = Student() # 创建实例s
    5. >>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
    6. Student
    7. >>> print(Student.name) # 打印类的name属性
    8. Student
    9. >>> s.name = 'Michael' # 给实例绑定name属性
    10. >>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
    11. Michael
    12. >>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
    13. Student
    14. >>> del s.name # 如果删除实例的name属性
    15. >>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
    16. Student

    2. 面向对象高级编程

    2.1 使用_slots_

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

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

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

    2.2 使用@property

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

    1. class Student(object):
    2. @property
    3. def score(self):
    4. return self._score
    5. @score.setter # 只定义getter方法,不定义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.3 多重继承

    通过多重继承,一个子类就可以同时获得多个子类的所有功能。
    在设计类的继承关系时,通常,主线是单一继承下来的,如果需要混入”额外”的功能,通常就需要多重继承来实现。这种设计称之为”MixIn”。
    我们不需要负责庞大的继承链,只有选择组合不同类的功能,就可以快速构造出所需的子类。

    2.4 定制类

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

    1. class Student(object):
    2. def __init__(self, name):
    3. self.name = name
    4. def __str__(self):
    5. return 'Student object (name=%s)' % self.name
    6. __repr__ = __str__

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

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

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

    1. class Fib(object):
    2. def __getitem__(self, n):
    3. a, b = 1, 1
    4. for x in range(n):
    5. a, b = b, a + b
    6. return a
    7. if isinstance(n, slice): # n是切片
    8. start = n.start
    9. stop = n.stop
    10. if start is None:
    11. start = 0
    12. a, b = 1, 1
    13. L = []
    14. for x in range(stop):
    15. if x >= start:
    16. L.append(a)
    17. a, b = b, a + b
    18. return L

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

    1. class Student(object):
    2. def __init__(self):
    3. self.name = 'Michael'
    4. def __getattr__(self, attr):
    5. if attr=='score':
    6. return 99

    _call_
    一个对象的实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用,但是任何类只需要定义一个_call_()方法,就可以直接对实例进行调用。

    1. class Student(object):
    2. def __init__(self, name):
    3. self.name = name
    4. def __call__(self):
    5. print('My name is %s.' % self.name)
    6. ---
    7. >>> s = Student('Michael')
    8. >>> s() # self参数不要传入
    9. My name is Michael.

    通过callable()函数,我们就可以判定一个对象是否是”可调用”对象。

    2.5 使用枚举类

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

    1. from enum import Enum
    2. Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
    3. # 这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个变量,或者枚举它的所有成员。
    4. for name, member in Month.__members__.items():
    5. print(name, '=>', member, ',', member.value)

    如果需要更精确的控制枚举类型,可以从Enum派生出自定义类。

    1. from enum import Enum, unique
    2. @unique # @unique装饰器可以帮助我们检查保证没有重复值
    3. class Weekday(Enum):
    4. Sun = 0 # Sun的value被设定为0
    5. Mon = 1
    6. Tue = 2
    7. Wed = 3
    8. Thu = 4
    9. Fri = 5
    10. Sat = 6
    11. # 访问枚举类型的若干种方法
    12. >>> day1 = Weekday.Mon
    13. >>> print(day1)
    14. Weekday.Mon
    15. >>> print(Weekday.Tue)
    16. Weekday.Tue
    17. >>> print(Weekday['Tue'])
    18. Weekday.Tue
    19. >>> print(Weekday.Tue.value)
    20. 2
    21. >>> print(day1 == Weekday.Mon)
    22. True
    23. >>> print(day1 == Weekday.Tue)
    24. False
    25. >>> print(Weekday(1))
    26. Weekday.Mon
    27. >>> print(day1 == Weekday(1))
    28. True
    29. >>> Weekday(7)
    30. Traceback (most recent call last):
    31. ...
    32. ValueError: 7 is not a valid Weekday
    33. >>> for name, member in Weekday.__members__.items():
    34. ... print(name, '=>', member)
    35. ...
    36. Sun => Weekday.Sun
    37. Mon => Weekday.Mon
    38. Tue => Weekday.Tue
    39. Wed => Weekday.Wed
    40. Thu => Weekday.Thu
    41. Fri => Weekday.Fri
    42. Sat => Weekday.Sat
    43. # 既可以根据成员名称引用枚举常量,又可以根据value的值获得枚举常量。

    2.6 使用元类

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

    1. >>> def fn(self, name='world'): # 先定义函数
    2. ... print('Hello, %s.' % name)
    3. ...
    4. >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
    5. >>> h = Hello()
    6. >>> h.hello()
    7. Hello, world.
    8. >>> print(type(Hello))
    9. <class 'type'>
    10. >>> print(type(h))
    11. <class '__main__.Hello'>

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


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





  • 相关阅读:
    Java JMX 监管
    Spring Boot REST(一)核心接口
    JSR 规范目录
    【平衡树】宠物收养所 HNOI 2004
    【树型DP】叶子的颜色 OUROJ 1698
    【匈牙利匹配】无题II HDU2236
    【贪心】Communication System POJ 1018
    【贪心】Moving Tables POJ 1083
    Calling Extraterrestrial Intelligence Again POJ 1411
    【贪心】Allowance POJ 3040
  • 原文地址:https://www.cnblogs.com/aniudcs/p/5971163.html
Copyright © 2011-2022 走看看