面向对象编程Object Oriented Programming

it2022-05-05  110

Object Oriented Programming,OOP。 OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。 面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。 而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接受其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

在python中,所有数据类型都可以视为对象,也可以自定义对象。自定义的对象数据类型就是面向对象中的类Class的概念。

面向对象的设计思想:抽象出Class,根据Class创建Instance

类和实例

Class and Instance

class Student(object): pass object,表示该类是从哪个类继承下来的,如果没有合适的继承类,就是用objict类,这是所有类都最终会继承的类。 创建实例是通过类名+()实现的。

可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去 class Student(object):

def __init__(self, name, score): self.name = name self.score = score

__init__方法的第一个参数永远是self,表示创建的实例本身。因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。 但是有了__init__方法在创建实例的时候,就不能传入空的参数,必须传入与__init__方法匹配的参数,但self不需要穿,python会自己把实例变量传进去: 和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。

数据封装

面向对象编程的一个重要特点就是数据封装 要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入

小总结类和实例

类是实例的模板,实例时一个个具体的对象,每个实例拥有的数据都互相独立,互不影响。 方法就是与实例绑定的函数,方法可以直接访问实例的数据

访问限制

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在py中,实例的变量如果以__开头,就变成private(见前文),只有内部可以访问,外部不能访问。 不能直接访问__name是因为py解释器对外把__那么变量改成了Student__name,所以可以通过这个访问

注意

在py中,变量名类似__xx__的,以双下划开头双下划结尾得,是特殊变量,特殊变量是可以直接访问的。 _xx 单下划线得意思是“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”

继承和多态

在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class) 子类继承父类得全部功能/方法。 当子类和父类都存在相同的方法时,我们说子类的方法覆盖了父类得方法,代码运行的时候,总是会调用子类的方法。 判断变量类型可以用 isinstance()判断 在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看作是父类,反之不可以 任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。 多态好处:当需要传入很多子类时,接受父类即可,按父类进行操作就行。 开闭原则:1)对扩展开放:允许新增Animal子类;2)对修改封闭:不需要修改依赖Animal类型的run_twice()等函数 继承可以多级

静态语言&动态语言

静态语言java,对于这种,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则无法调用父类方法 对于动态语言(py),不一定需要传入Animal类型,只需要保证传入的对象有一个run()方法就行。 对动态语言来说,不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看作是鸭子。(“鸭子类型”)

获取对象信息

type

type()基本类型都能用这个函数判断,如果一个变量指向函数或者类,也可以用type判断,返回对应的Class类型,如果在if中判断,比较两个变量的type类型是否相同。

判断一个对象是否是函数,使用types模块中定义的常量。

使用isinstance(),对于class的继承关系来说,使用type()就很不方便。判断class的类型,使用这个函数。 如果继承关系是:object-Animal-Dog-Husky,那么isinstance()就可以告诉我们,一个对象是否是某种类型。 能用type()判断的基本类型也可以用isinstance判断,并且还可以判断一个变量是否是某些类型中的一种,isinstance([1, 2, 3], (list, tuple))

使用dir()

获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list。 类似__xxx__的属性和方法在py中都是有特殊用途的,比如__len__方法返回长度,在py中,如果调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,len(‘A’)=‘A’.len() 自己写的类,如果也想用len(myObj),就自己写一个__len__()方法

其他方法合集

lower()返回小写字符串 hasattr(obj,‘y’) 有属性’x’吗 setattr(obj,‘y’,19)设置一个属性’y‘ getattr(obj,‘y’) 获取属性’y‘ 可以给getattr传入一个default参数,如果不存在返回默认值比如:getattr(obj,‘y’,404)

实例属性和类属性

动态语言根据类创建的实例可以任意绑定属性。给实例绑定属性的方法是通过实例变量,或者通过self变量 如果Student类本身需要绑定一个属性,可以直接在class中定义属性,类属性。

使用__slots__

动态绑定允许我们在程序运行的过程中动态给class加上功能。 如果我们想要限制实例的属性,比如只允许对Student实例添加name和age属性。 为了达到限制的目的,py允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性: class Student(object): slots=(‘name’,‘age’)#用tuple定义允许绑定的属性名称 这个定义的属性仅对当前类实例起作用,对继承的子类不起作用,除非子类中也定义__slots__,这样子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

使用@property

既能检查参数,又可以用类似属性这样简单的方式来访问类的变量。 装饰器可以给函数动态加上功能,对于类一样起作用,python内置的@property装饰器是负责把一个方法变成属性调用: class Student(object): return self._score @score.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

多重继承

在继承的括号里继承多个父类。

Mixln

目的是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个Mixln的功能

getattr

没有找到属性的情况下,动态返回一个属性,函数

call

任何类,只需要定义一个 call方法就可以直接对实例进行调用

使用枚举类

类型是int ,而且仍是变量 python提供了Enum类 value属性则是自动赋给成员的int常量,默认从1开始计数

使用元类

type()

动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运动时动态创建的。 type()函数可以查看一个类型或变量的类型。 创建class的定义是运行时动态创建的,而创建class的方法就是使用type()函数,type既可以返回一个对象的类型,又可以创建出新的类型 要创建一个class对象,type()函数依次传入3个参数:1、class的名称 ;2、继承的父类集合,如果只有一个父类,注意tuple的单元素写法;3、class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。 通过type()函数创建的类和直接写class是完全一样的,因为python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type函数创建出class

metaclass 重点非常重

控制类的创建行为还有metaclass metaclass直译为元类,简单解释:当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类后创建实例 如果想创建出类,根据metaclass创建出类,所以:先定义metaclass然后创建类。 连起来,先定义metaclass,就可以创建类,最后创建实例。 所以,metaclass允许你创建类或者修改类,换句话说,你可以把类看成是metaclass创建出来的’实例‘ 一般很少用到这个东西,但是!总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。

ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。

要编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。

稍微了解一点编写ORM

编写底层模块的第一步,先把调用接口写出来。比如,使用者如果使用这个ORM框架,想定义一个User类来操作对应的数据库表User,写出这样的代码: class User(Model): id = IntegerField(’id‘) name = StringField(’username‘) email = StringField(’email‘) password = StringField(’password‘)


最新回复(0)