【Python编程与数据分析】面向对象

面向对象

对象

  1. Python 支持多种不同数据类型
  2. 以上所有数据都是一个对象, 每个对象都有:
    • 身份(identity),即是存储地址,可以通过id()这个方法来查询
    • 类型(type),即对象所属的类型,可以用type()方法来查询
    • 值,都会有各自的属性、方法
  3. 一个对象是一个类型的实例(instance)

面向对象编程(OOP)

  1. Python中,一切皆对象(并有一个类型)
  2. 可以创建某一类型的对象
  3. 可以对 对象进行操作
  4. 可以销毁对象
  5. 显式的使用del 或者 不去管它
  6. python 系统会 回收被销毁或者不可读取的对象-被称为“垃圾回收机制”

如何理解一切皆对象

  1. 在面向对象体系里面,存在两种关系:
  • 父子关系,即继承关系: python里要查看一个类型的父类,使用它的__bases__属性可以查看
  • 类型实例关系: 使用它的__class__属性可以查看,或者使用type()函数查看
  1. object是继承关系的顶端,object是所有类型的父类(直接或间接)
  2. type是类型实例关系的顶端,所有对象都是它的实例的
  3. 二者关系:
  • object是type的一个实例
  • Type是object的子类
>>>object.__class__ 
>>>type(object)
#type
>>>object.__bases__ 
#()
#object是继承关系顶端,没有父类
>>> type.__bases__
#(object,)
#type是object的子类
>>> type.__class__ 
#type
#type的类型是type

5.

面向对象的好处

  1. 将数据和方法进行打包 通过接口提供给使用者
  2. 分而治之(divide-and-conquer) 的开发
  • 可以独立的开发和测试每个类
  • 使程序更加模块化,降低复杂度
  1. 类(classes) 让代码复用更容易
  • 很多Python的模块都有自定义的新类
  • 每个类都有自己独立的环境和命名(不与其他函数名冲突)
  • 使用继承使得子类重定义或者扩展父类的一些行为

创建和使用类的区别

  1. 创建一个类有别于使用类来创建一个实例(instance)
  2. 创建 类包括: 定义类名;定义类的属性、方法;例:写一段实现 list的代码
  3. 使用 类包括:创建类的实例;对实例进行操作;例, L=[1,2]len(L)

自定义类(类型)

  1. class 关键字 :定义一个新的类型

什么是属性(attributes)

  1. 类中的数据和程序
  2. 数据属性
  • 构成类的数据对象
  • 例: Coordinate 类 由 2个数(坐标)构成
  1. 方法 (程序属性)
  • 方法就是类中的函数,只作用于这个类或类的实例
  • 与对象进行交互
  • 例:在coordinate类中定义一个 distance的方法,求两个坐标对象的距离,但distance无法作用在其他类型的对象上,比如list

创建类的实例

  1. 构造方法(constructor):定义如何创建一个类的实例
  2. 使用特殊方法: __init__ 初始化一些数据属性
  3. 定义好了类之后,创建一个实例吧
  4. 实例中的数据属性,比如c.x中的x,叫做实例变量
  5. 不用传递任何值给self,python会自动处理

什么是方法(method)

  1. 方法是类中的程序属性,一个只作用于这个类的function(函数)
  2. Python总是会把对象当作第一个参数: 惯例是把self当作所有方法的第一个参数
  3. .操作符 可以获取到任意属性: • 对象的实例属性 • 对象的方法(method)

写一个Coordinate类的方法

如何使用方法


2.

打印一个对象

>>> c = Coordinate(3,4)
>>> print(c)
#<__main__.Coordinate object at 0x7fa918510488>
  1. 直接打印对象时,无法打印出有效信息
  2. 给类定义一个__str__方法
  3. 使用print()打印对象时,Python 会调用__str__方法
  4. 可以自定义打印的内容和格式! 比如打印Coordinate对象时, 我们想要
>>> print(c)
<3,4>

自定义打印方法

类和类型(Types)


2. 实现类 VS 使用类 | 类定义 VS 类的实例

GETTER AND SETTER 方法

不能从外部访问的属性、方法

一个实例 和 .(dot)符号

  1. 一个对象的实例的创建叫做实例化:a = Animal(3)
  2. .符号用来获取属性(数据和方法) ,但是最好使用getters和setters来获取数据属性

为何不用.符号:信息隐藏

Python的信息隐藏并不到位

  1. 以下的行为均不提倡!
  2. 允许从类定义的外部获取数据: print(a.age)
  3. 允许从类定义的外部写入数据: a.age = 'infinite’ #age本应是一个整数
  4. 允许从类定义的外部创建新的数据属性: a.size = "tiny" #(size并没有在类中定义)

方法的默认参数

类的层级:继承

父类(Parent class)

子类

class Cat(Animal):  # 继承所有Animal类的属性
  def speak(self):  # 添加新的方法speak()
    print("meow")
  def __str__(self):  # 重写/复写(overrides)__str__
    return "cat:"+str(self.name)+":"+str(self.age)

  1. 添加一个新的功能/行为 speak()
  • Cat 类型的实例可以调用新的方法
  • Animal类型的实例如果调用新方法speak() 会抛出异常(throws error)
  1. init 构造方法并没有消失,从Animal父类中继承

实例使用的是哪个方法?

• 子类中的方法可以与父类中的方法同名
• 对于一个类的实例,会在自己的类定义中寻找方法名
• 如果没找到,去上级父类中寻找方法名(先在父类中找,然后去“爷爷”类中找,以此类推)
• 调用最先找到的那个方法

类变量(class variable)

  1. 所有的实例共享类变量(class variables)
  2. 类变量 vs 成员变量

Rabbit GETTER 方法(注意.zfill()方法)

魔法方法重载

  1. 比较两个Rabbits的特殊方法

运算符重载的魔法方法

super()

  1. super()是python内置函数
  2. 单继承中,super()主要是用来调用父类的方法
class A:
  def method(self):
    print("I am in parent")
class B(A):
  def method(self):
    print("I am in child.")
    super().method()  # 通过super()调用父类A中的method方法
b = B()
b.method()
#I am in child. 
#I am in parent

魔法方法重载


2.

class Mycls:
  def __new__(cls):
    print('new')
    return super().__new__(cls)
  def __init__(self):
    print('init’)
#用Mycls类创建一个实例化对象my
my=Mycls()
#new
#init
  1. __new__方法的应用场景(eg. 单例模式)
  2. __call__方法
  • 该方法的功能类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用
class Mycls:

  def __call__(self,name,age):  # 定义__call__方法
    print("调用__call__()方法",name,age)

person = Mycls()
person("你儿子","8岁")
#调用__call__()方法 你儿子 8岁
• 可以看到,通过在 Mycls 类中实现 __call__() 方法,使的 person 实例对象变为了可调用对象。
• person(“你儿子”,“8岁”) 等同于 person.__call__ ("你儿子","8岁")

抽象类

  1. 抽象类:
  • 职责:包含子类应该实现的一组抽象方法
  • 特征:不能实例化
  1. 抽象类 : register的用法(左) 和 注意事项(右)
  2. 静态方法
  3. 类方法
  4. str()方法和__repr__()方法
  • str()和__repr__()都是用来显示/打印的
  • print()方法打印一个实例对象时,首先会尝试__str__(),
  • Python 定义了__str__()和__repr__()两种方法,str()用于显示给用户,而__repr__()用于显示给开发人员。
  1. 给参数注释

slots

  1. slots 属性: 可以避免用户频繁的给实例对象动态地添加属性或方法
class Student(object): 
  pass
s = Student() 
s.name = 'Michael' # 动态给实例绑定一个属性
print(s.name) 
#Michael
#可以给实例动态绑定一个方法
def set_age(self, age): # 定义一个函数作为实例方法(注意,带上了self)
  self.age = age

from types import MethodType
s.set_age = MethodType(set_age, s) # 给实例s绑定set_age()方法
s.set_age(25) # 调用实例方法
s.age # 测试结果
  1. 给一个实例绑定的方法,对另一个实例是不起作用的
s2 = Student() # 创建新的实例s2
s2.set_age(25) # s2调用方法set_age,报错
  1. 若要给所有实例绑定方法,可以给class绑定方法
def set_score(self, score):
  self.score = score
Student.set_score = set_score #给class绑定方法
#Student.set_age = MethodType(set_age, Student) 也可以
s2.set_score(99) #此时s2可以调用新绑定的方法和属性
s2.score
#99
  1. 如果要限制Student类中的实例属性,比如只允许实例添加name和age属性时,在定义class时,定义__slots__
class Student(object): 
  __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student() # 创建新的实例
s.name = ‘Michael’ # 绑定属性‘name’
s.age = 25 # 绑定属性‘age’
s.score = 99 
# 绑定属性‘score’, 报错! score不在__slots__里
  1. __slots__注意事项
  • __slots__定义的属性仅对当前类实例起作用,对继承的子类不起作用
#GraduateStudent 继承Student
class GraduateStudent(Student): 
  pass 
g = GraduateStudent() 
g.score = 9999
#Student的子类实例仍然能够动态绑定score
  • 除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
  • slots 只能限制为实例对象动态添加属性和方法,而无法限制动态地为类添加属性和方法
class Student(object): 
  __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student() # 创建新的实例
s.score = 99 # 绑定属性‘score’, 报错! score不在__slots__里
Student.score = 111
#__slot__限制不住为Student类动态添加一个属性或方法
  1. __slots__的好处

装饰器

  1. 在代码运行期间动态的给函数或类增加功能的方式,称之为“装饰器”(Decorator)
#定义一个函数now
def now():
  print('2015-3-25’)
#功能太简单,想事后给它增加一些功能, 比如,打印日志的功能
#定义一个日志函数log,接受一个函数对象func,打印”call func._name__”,并返回一个函数调用
def log(func):
  def wrapper(*args, **kw):
    print('call %s():' % func.__name__)#打印日志
    return func(*args, **kw)#调用传入的func函数并返回
  return wrapper


2. 前面的写法很复杂,有了装饰器之后,用Python的@语法(语法糖),把decorator置于函数的定义处。now函数就被log装饰了,功能也增强了,具备了log函数的功能。

@log
def now():
  print('2015-3-25’)
now()
#call now(): 
#2015-3-25
  1. 把@log放到now()函数的定义处,相当于执行了语句: now = log(now)

装饰器 @property

  1. “实例对象.属性”的方式访问类中定义的属性,其实是欠妥的,因为它破坏了类的封装原则
    ◼ 应在类中设置多个getter或setter方法
    ◼ 通过 ”实例对象.方法” 的方式操作属性
    ◼ 但这种反复调用getter和setter操作类属性的方式比较麻烦
s = Student() 
s.score = 9999 #将属性暴露出去,不安全,无法检查值的合法性
  1. 同@classmethod, @abstractmethod一样, @property也是一个内置装饰器
    ◼ 负责把一个类的getter方法伪装成属性调用
    ◼ @property还会创建@xxx.setter和@xxx.deleter装饰器

  2. property()内置函数用法
  • 效果和使用@property装饰器一样
  • 使用格式:
    ◼ 属性名=property(fget=None, fset=None, fdel=None, doc=None)
    ◼ fget :指定getter方法,fset :指定setter方法,fdel:指定删除该属性的方法,doc : 文档字符串,用于说明此函数的作用。

命名规则

由于 Python 3 支持 UTF-8 字符集,因此 Python 3 的标识符可以使用 UTF-8 所能表示的多种语言的字符。Python 语言是区分大小写的,因此 abc 和 Abc 是两个不同的标识符。

  1. 标识符可以由字母、数字、下画线(_)组成,其中数字不能打头。
  2. 标识符不能是 Python 关键字,但可以包含关键字。
  3. 标识符不能包含空格。
posted @ 2022-12-02 12:27  Mothlove  阅读(52)  评论(0编辑  收藏  举报