一、面向对象
编程的方式
- 面向过程:根据业务逻辑从上到下垒代码。
- 函数式:将某功能封装到函数中,日后便无需重复编写,仅调用函数即可。
- 面向对象:对函数进行分类和封装,让开发“更快更好更强……”
什么是面向对象:
面向对象就不像面向过程那样按照功能划分模块了,它所关注的是软件系统有哪些参与者,把这些参与者称为对象,找出这些软件系统的参与者也就是对象之后,分析这些对象有哪些特征、哪些行为,以及对象之间的关系,所以说面向对象的开发核心是对象。
什么是类:
面向对象编程的2个非常重要的概念:类和对象
类是对象的类型,具有相同属性和行为事物的统称。类是抽象的(不存在的),在使用的时候通常会找到这个类的一个具体存在。
什么是对象:
万物皆对象,对象拥有自己的特征和行为。
类和对象的关系:
类是对象的类型,对象是类的实例。类是抽象的概念,而对象是一个你能够摸得着但得到的实体。二者相辅相成,谁也离不开谁。
类由三个部分构成:
- 类的名称:类型
- 属性:对象的属性
- 方法:对象的方法
二、创建和使用:
类定义:
class 类名:
属性列表
方法列表
例:
'''
类定义
class 类名():
# 类文档说明
属性
方法
'''
class person():
'''
这是一个人类
'''
country = '中国' # 声明类属性并赋值
# 实例属性通过构造方法来声明
# self不是关键字,代表的是当前对象
def __init__(self, name, age, sex): # 构造方法
# 构造方法不需要调用,在实例化的时候自动调用
print('我是构造方法,在实例化的时候调用')
self.name = name # 通过self创建实例属性,并且赋值
self.age = age
self.sex = sex
# 创建普通方法
def getName(self):
print('我的名字叫:%s,我来自%s'%(self.name, person.country)) # 在方法里面使用实例属性
# 实例化对象
people1 = person('joe', 1, '男') # 在实例化的时候传进参数
# 这个people1就要具有三个属性,并且可以使用getName方法
# 访问属性
print(people1.name) # 通过对象名.属性名 访问实例属性(对象属性)
# 通过对象调用实例方法
people1.getName()
# output:我的名字叫:joe,我来自中国
__init__()构造方法和self:
- __init__()是一个特殊的方法,属于类的专有方法,被称为类的构造函数或初始化方法,方法的前面和后面都有两个下划线。
- 这是为了避免Python默认方法和普通方法发生名称的冲突。每当创建类的实例化对象的时候,__init__()方法都会默认被运行。作用就是初始化已实例化后的对象。
- 在方法定义中,第一个参数self是必不可少的。类的方法和普通的函数的区别就是self,self并不是Python关键字,你完全可以用其他单词取代,只是按照惯例和标准的规定,推荐使用self。
类的属性分类(创建一个people类):
- 类属性
- 实例属性
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。如果需要用,在函数中使用类名.类属性。
- 实例变量:定义在方法中的变量,只作用于当前实例的类。
针对类的属性的一些方法:
- 可以使用实例化对象名+.来访问对象的属性
- 也可以使用一下函数的方式来访问属性
'''
getattr(obj, name[, default]): 访问对象的属性
hasattr(obj, name): 检查是否存在一个属性
hasattr(obj, name, value): 设置一个属性。如果属性不存在,会创建一个新属性
delattr(obj, name): 删除属性
'''
print(getattr(people1, 'name')) # output:joe
print(hasattr(people1, 'name')) # output:True
setattr(people1, 'name', 'susan')
print(people1.name) # output:susan
delattr(people1, 'name')
print(people1.name)
# output:AttributeError: 'person' object has no attribute 'name'
内置类属性:
Python内置类属性:
- __dict__:类的属性(包含一个字典,由 类的属性名:值 组成)实例化类名.__dict__
- __doc__:类的文档字符串 (类名.)实例化类名.__doc__
- __name__:类名,实现方式 类名.__name__
- __bases__:类的所有父类构成元素(包含了由所有父类组成的元祖)
'''
内置类属性
'''
print(people1.__dict__)
# output:{'age': 1, 'sex': '男', 'name': 'susan'}
# 会将实例对象的属性和值通过字典返回
print(people1.__doc__)
# output:这是一个人类
print(person.__name__) # 返回类名
# output:person
print(person.__bases__)
# output:(<class 'object'>,)
__name__
- __name__: 如果是放在Modules模块中,就表示是模块的名字;如果是放在Class类中,就表示类的名字;
- __main__: 模块,xxx.py文件本身,被直接执行时,对应的模块名就是__main__了。可以在if __name__ == "__main__":中添加你想要的,用于测试模块,演示模块用法等代码。
- 作为模块,被别的Python程序导入(import)时,模块名就是本身文件名XXX了。
例:
假设有两个文件,一个是name1.py:
def a():
print('我是a方法')
print(__name__)
# output:__main__
输出:
另一个文件是name2.ph:
import name1
输出:
测试时,可以把name1.py写成:
def a():
print('我是a方法')
if __name__ == '__main__':
a()
即,只在当前脚本运行时,运行a(),其他脚本引入时,不运行a()。
三、继承和多态
继承:
程序中,继承描述的是事物之间的所属关系,例如猫狗都属于动物,程序中可以描述为猫和狗都继承自动物。同理,波斯猫和家猫都继承自猫。程序中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称之为子类(subclass),而被继承的class称之为基类、父类或超类。子类继承了其父类所有属性和方法,同时还可以自定义自己的属性和方法。
python时多继承的,即一个子类可以继承多个父类。
class Animal():
def __init__(self, name, food):
self.name = name
self.food = food
def eat(self):
print('%s爱吃%s'%(self.name, self.food))
# Dog类继承Animal
class Dog(Animal): # python中的继承,只要把父类名写在括号里就行
def __init__(self, name, food, drink):
super().__init__(name, food) # 加载父类中的初始化方法
self.drink = drink
def drinks(self):
print('%s爱喝%s'%(self.name, self.drink))
# Cat类继承Animal
class Cat(Animal):
def __init__(self, name, food, drink):
super().__init__(name, food)
self.drink = drink
def drinks(self):
print('%s爱喝%s'%(self.name, self.drink))
# 子类(Dog和Cat)继承了父类(Animal)的全部功能,自动拥有了父类的eat()方法
dog1 = Dog('小狗','骨头','肉汤')
dog1.eat()
dog1.drinks()
cat1 = Cat('小猫','鱼','牛奶')
cat1.eat()
cat1.drinks()
输出:
多继承:
python中多继承的语法格式如下:
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
...
...
<statement-1>
注意圆括号中父类的顺序,如果继承的父类中有相同的方法名,而在子类中使用时未指定,python将从左至右查找父类中是否包含方法啊。
class A():
def a(self):
print('我是A里的a方法')
class B():
def b(self):
print('我是B里的b方法')
class C():
def c(self):
print('我是C里的c方法')
class D(A, B, C):
def d(self):
print('我是D里的d方法')
dd = D()
dd.a()
dd.b()
dd.c()
dd.d()
输出:
四、类属性和实例属性
属性:
尽量把需要用户传入的属性作为实例属性,而把同类都一样的属性作为类属性。实例属性在每创造一个类时都会初始化一遍,不同的实例的实例属性可能不相同,不同实例的类属性都形同。
1.实例属性:
在__init__(self, ...)中初始化
内部调用时都需要加上self。
外部调用时用“对象名.属性名”调用
2.类属性:
在__init__()里初始化
在内部用classname.类属性名调用
外部既可以用classname.类属性名,又可以用instancename.类属性名来调用
3.私有属性:
(1)双下划线__开头:外部不可通过“对象名.属性名”来访问或者更改实际将其转化为了“_类名__属性名”
class person():
'''
这是一个人类
'''
country = '中国' # 声明类属性并赋值
# 实例属性通过构造方法来声明
# self不是关键字,代表的是当前对象
def __init__(self, name, age, sex, address): # 构造方法
# 构造方法不需要调用,在实例化的时候自动调用
print('我是构造方法,在实例化的时候调用')
self.name = name # 通过self创建实例属性,并且赋值
self.age = age
self.sex = sex
self.__address = address # 双下划线开头的属性是私有属性
# 创建普通方法
def getName(self):
print('我的名字叫:%s,我来自%s'%(self.name, person.country)) # 在方法里面使用实例属性
# 实例化对象
people1 = person('joe', 1, '男') # 在实例化的时候传进参数
# 通过对象名.属性名访问私有属性
print(people1.__address)
输出:AttributeError: 'person' object has no attribute '__address'
私有属性只能在类内使用!
或者强制访问:
print(people1._person__address)
输出:
再或者:外部要修改私有属性,预留一个借口去访问或者修改私有属性。例如,可在类内加如下方法。
def getAddre(self):
return self.__address
单下划线、双下划线、头尾双下划线说明:
- __foo__: 定义的是特殊方法,类似__init__()之类的。
- _foo: 以单下划线开头的表示的是protected类型的变量,即保护类型只能允许其本身与子类进行访问(创建的实例可以访问),不能用于from module inport *
- __foo: 双下划线的表示的是私有类型(private)的变量,只能是允许这个类本身进行访问。
五、访问限制
- 设置为私有属性不能直接通过对象访问属性,但可以通过“实例化对象._类名__属性名”直接访问。但不建议这样操作。不同版本的Python解释器可能会把“__属性名”改成不同的变量名。总的来说,python本身没有任何机制阻止你干坏事,一切全靠自觉。
- 通过“对象名.__属性名”直接修改私有属性。表面上看好像修改了,其实并没有。因为python解释器已经将对象内部的属性名解释成“_类名__属性名”。如果在外部修改相当于另外声明一个属性。
people1.__address = '上海'
# 看上去改了,其实没有。你给当前对象声明了一个新属性
print(people1.__address)
print(people1.getAddre())
输出:
六、类方法与静态方法
1. 普通方法:
def fun_name(self, ...):
pass
外部用实例调用
2.静态方法:通过装饰器 @staticmethod 装饰
- 不能访问实例属性
- 参数不能传入self
- 与类相关但是不依赖于实例的方法
# 创建一个静态方法
@staticmethod
def aa(): # 不需要传递实例
print('我的名字叫:%s'%self.name)
people1 = person('joe', 1, '男')
people1.aa()
会报错:name 'self' is not defined
所以只能访问类属性:
# 创建普通方法
def getName(self):
print('我的名字叫:%s,我来自%s'%(self.name, person.country)) # 在方法里面使用实例属性
# 创建一个静态方法
@staticmethod
def aa(): # 不需要传递实例
print('我来自:%s'%person.country)
people1 = person('joe', 1, '男')
people1.aa()
输出:
3. 类方法:@classmethod
- 不能访问实例属性
- 参数必须传入cls(即代表了此类对象,区别,self代表实例对象),并且用此来调用雷属性:cls.类属性名
# 类方法
@classmethod
def bb(cls): # class 也不是关键字
# 类方法不能访问实例属性
print('我的名字叫:%s'%cls.country) # 就用cls访问类属性
people1 = person('joe', 1, '男')
people1.bb()
输出:
静态方法与类方法都可以通过类或实例来调用。特点都是不能调用实例属性。静态方法不需要接受参数,使用类名.类属性