Python Learning(八)-面向对象

八 面向对象

对象(Object)

什么是对象?

  • 对象是内存中专门用来存储数据的一块区域

  • 对象中可以存放各种数据

  • 对象由三部分组成(id,type,value)

面向对象(oop)

  • Python是一门面向对象的编程语言
  • Python中的所有操作,都是通过对象来进行的
  • 对应的是面向过程的编程语言,指的是将程序的逻辑分解为一步步的抽象来完成程序
  • 面向过程编写的代码往往只适用于一个功能,不方便扩展,可复用性低
  • 面向对象的编程语言关注的是对象而不是过程,一切都是对象
类(class)
  • 我们目前学习的对象都是Python的内置对象
  • 自定义对象(class)
  • 类,简单理解他就是一个图纸,程序中需要根据类创建对象
  • 我们称对象是类的instance实例
  • 如果多个对象是通过一个类创建的,那么我们称这些对象是一类对象
  • 自定义的类都需要使用大写字母开头,大驼峰命名法

定义一个简单的类

类创建语法:

class 类名([父类]):

​ 代码块

class MyClass():
    pass
mc=MyClass()
print(MyClass)
print(isinstance(mc,MyClass))
# __main__.MyClass
# True
mc =MyClass() #mc 就是MyClass的实例
mc_2=MyClass()

实际上类也是一个对象,就是用来创建对象的对象,可以看下每个类的type

使用类创建对象的流程 mc=MyClass()

1. 创建一个变量mc
2. 在内存中创建一个新的对象
3. 分配地址,type就等于MyClass
4. 将对象的id赋值给变量mc	

现在我们通过MyClass创建的对象什么都没有,相当于一个空盒子,可以像对象中添加变量称为属性

属性语法:

对象.属性=属性值

mc.name=‘tom’

类的定义
  • 类和对象是对现实生活中的内容抽象
  • 实际上所有的事物都有俩部分构成
    1. 数据(属性)
    2. 行为(方法)
class Person:
	name='tom'
	def sayHello():
		print('Hello')
p1=Person()
p2=Person()


print(p1.name,p2.name)
p1.sayHello()
# TypeError: sayHello() takes no arguments (1 given)

属性和方法都定义在类对象中,为什么实例可以访问到类中的属性和方法

  • 属性和方法的查找流程

    1. 当我们调用一个对象的属性时,解析器会先在当前对象中寻找是否含有该属性
    2. 如果有则直接返回当前的对象的属性值
    3. 如果没有则去当前对象的类对象中去寻找,如果有则返回类对象的属性值(就是type对应的类对象)
  • 所以共有的属性和方法应该存在类对象中,实例独有的属性和方法需要自定义

    比如:人共有的吃饭/睡觉等方法是共有的,名字是自定义的

方法调用和函数调用的区别:

如果是函数的调用传几个参数就是几个参数,如果是方法的调用默认至少有一个参数.

注意:方法调用时,第一个参数由解析器自动传递,所以定义方法时,至少要定义一个形参

这里第一个参数,就是调用方法的对象本身,如果p1调的,则第一个参数就是p1对象,p2调的则第一个参数就是p2对象,一般我们都会将这个参数命名为self

class Person:
	name='tom'
	def sayHello(self):
		print(self)
p1=Person()
p1.sayHello()
print(p1)
p2=Person()
p2.sayHello()
print(p2)
#<__main__.Person instance at 0x7f688ec60638>
#<__main__.Person instance at 0x7f688ec60638>
#<__main__.Person instance at 0x7f688ec60680>
#<__main__.Person instance at 0x7f688ec60680>
对象的初始化

在类中可以定义一些特殊的方法(魔术方法),以_开头_结尾的方法

特殊方法不需要我们自己调用,会在类对象创建的时候自动调用,这样可以避免必要属性不传导致的错误.

创建对象的流程 :

  1. 创建一个变量
  2. 在内存中创建一个对象
  3. _init_(self)方法执行
  4. 将对象的id赋值给变量
一个标准类的流程
class Person:
	def __init__(self,name):
		self.name=name
	def sayHello(self):
		print('i am '+ self.name)
p1=Person('tom')
p1.sayHello()
print(p1.name)
p2=Person('jack')
p2.sayHello()
print(p2.name)
# i am tom
# tom
# i am jack
# jack

类的基本结构

class类名([父类]):

​ 公共的属性

​ #对象的初始化方法

​ def _init_(self,…):

​ …

​ def method(self):

​ …

目前对象的属性可以随意修改,非常不安全,不论对错例如:只需要p.name=…就行,所以引入封装

封装

封装是面向对象的三大特性之一

封装指的是隐藏对象中一些不希望被外部所访问到的属性或方法

如何隐藏对象中的一个属性?

  1. 修改对象的属性名为外部知道的名字

  2. getter and setter

  3. getter setter 方法其实也可以有妙用 比如get方法 用户获取商品价格的时候计算PU UV

    set方法 用户修改密码的时候给出提醒

  4. 使用getter方法可以表示一些计算的属性 比如计算长方形的面积

  5. 可以使用__双下划线开头的属性,这个是隐藏属性只能在类的内部访问,(相当于java中的private),无法通过对象访问

  6. 其实隐藏属性只不过是Python自动为属性修改了一个名字,__属性实际上是改为了,_类命_属性名

  7. 一般情况下我们使用_单下划线开头属性表示私有属性,没有特殊需要不要修改私有属性(只是不希望你改)

    class Person:
    	def __init__(self,name):
    		self._name=name
    	def get_name(self):
    		return self._name
    	def set_name(self,name):
    		self._name=name
    
    p=Person('hetao')
    print(p.get_name())
    
    
class Person:
	def __init__(self,name,age,price):
		self.hidden_name=name
		self.hidden_age=age
		self.hidden_price=price
	def get_name(self):
		return self.hidden_name
	def set_name(self,name):
		self.hidden_name=name
	def get_age(self):
		return self.hidden_age
	def set_age(self,age):
		if age>0:
			self.hidden_age=age
	def get_price(self):
		return self.hidden_price
	def set_price(self,price):
		self.hidden_price=price
	def sayHello(self):
		print('i am '+ self.hidden_name)
p=Person('hetao',2,'1800')
print(p.get_price())
p.set_price(2000)
print(p.get_price())
# 1800
# 2000
装饰器

property装饰器,用来将一个get方法,装换为对象的属性

添加为property装饰器以后,我们就可以像调用属性一样使用get方法

使用property装饰的方法,必须和属性名是一样的 @属性名.setter(注意:有setter必须有getter)

class Person:
	def __init__(self,name):
		self._name=name
	@property
	def name(self):
		print('get is running')
		return self._name

	@name.setter
	def name(self,name):
		print('set is running')
		self._name=name

p=Person('hetao')
print(p.name)
p.name='tom'
print(p.name)
# get is running
# hetao
# set is running
# get is running
# tom
继承

在定义类的同时,可以在括号中指定当前类的父类,子类可以继承父类中所有的属性和方法

class Animal:
	def run(self):
		print('running funny')
	def sleep(self):
		print('sleeping deeply')

class Dog(Animal):
	def bark(self):
		print('wang wang wang')

d=Dog()
d.run()
print(issubclass(Dog,Animal))
print(isinstance(d,Animal))
# running funny
# True
# True

如果在创建类时省略了父类,默认父类为Object,Object是所有类的父类,所有的类都继承Object

如果子类的方法和父类方法重复,这个时候调用谁?

重写
class Animal:
	def run(self):
		print('running funny')
	def sleep(self):
		print('sleeping deeply')

class Dog(Animal):
	def run(self):
		print('dog is running')
	def bark(self):
		print('wang wang wang')

d=Dog()
d.run()
# dog is running

调用一个对象的方法时,如果有调用当前对象的方法,没有的话调用父类中去寻找,直到Object

父类中所有的方法都会被子类继承,包括特殊方法,如果不初始化父类中的属性会报错,所有希望直接调用父类中的init方法来初始化父类中的属性super()

super()

通过super()返回对象调用父类方法时,不需要传递self(python3中)

class Animal(object):
	def __init__(self,name,age):
		self._name=name
		self._age=age
	@property
	def name(self):
		self._name=name
	@name.setter
	def name(self,name):
		self._name=name
	@property
	def age(self):
		self._age=age
	@age.setter
	def age(self,age):
		self._age=age
class Dog(Animal):
	def __init__(self,name,age):
		super(Dog,self).__init__(name,age)
	@property
	def name(self):
		self._name=name
	@name.setter
	def name(self,name):
		self._name=name
	@property
	def age(self):
		self._age=age
	@age.setter
	def age(self,age):
		self._age=age


d=Dog('tom',18)
# python2
class Animal:
	def __init__(self,name,age):
		self._name=name
		self._age=age
	@property
	def name(self):
		self._name=name
	@name.setter
	def name(self,name):
		self._name=name
	@property
	def age(self):
		self._age=age
	@age.setter
	def age(self,age):
		self._age=age
class Dog(Animal):
	def __init__(self,name,age):
		super( ).__init__(name)
		self._age=age
	@property
	def name(self):
		self._name=name
	@name.setter
	def name(self,name):
		self._name=name
	@property
	def age(self):
		self._age=age
	@age.setter
	def age(self,age):
		self._age=age


d=Dog('tom',18)
# python3

类名.__bases__这个属性可以用来获取当前类的所有父类

class A(object):
	def test(self):
		print('a')
class B(object):
	def test(self):
		print('b')
class C(A,B):
	def test(self):
		print('c')
print(C.__bases__)
#(<class '__main__.A'>, <class '__main__.B'>)

如果多个父类中有重名的方法,会先调前面的

class A(object):
	def test(self):
		print('a')
class B(object):
	def test(self):
		print('b')
class C(B,A):
	pass
c=C()
c.test() 
# b

没有特殊情况下我们一般不会用多重继承

比如

		object 2
    A 1				B 3
    
    		c 4
    		
    		D 5
	class D(A,B,C):
		pass
	d=D()
	d.test()这个时候的调用顺序如上
	# 先找第一个父类A,如果A中没有找A的父类Object,Object没有找B,b没有找b的父类(因为Object已经找过不再找了,然后是c,c找不到找c的父类(因为abObject都找过不再找),然后是找d)
	# 所以多重继承太过复杂
多态

多态从字面理解是多种形态

java中的多态指的是父类的应用对应子类的对象,这样更加灵活使用

Animal animal =new Cat();

在Python中调用不同的子类将会产生不同的行为,而无需知道这种子类实际上是什么,这是多态重要的应用场景

多态的具体好处可以用一句话来概括: 一个接口,多种实现

  • 增加了程序的灵活性,不论对象如何变化,使用者都是通过同一种形式去调用
  • 增加了程序的额外可扩展性
多态和多态性(鸭子类型)

多态指的是一类事物的多种形态,比如animal类可以有多种子类(猫/狗/猪等)

多态性指的具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名,调用不同内容的函数

比如len()函数可以传递列表,元组等

任意定义一个函数只要有__len__方法也可以使用len()函数

class A(object):
	def __len__(self):
		return 10

c=A()
print(len(c))
# 10

面向对象的三大特征

  • 封装

    确保了对象中的数据安全

  • 继承

    保证了对象的可扩展性

  • 多态

    保证了程序的灵活性

类中的属性和方法
类属性
class A(object):
	count=0
	def __init__(self):
		self.name='tom'
a=A()
print(a.count)
A.count=100
print(A.count)
# 0
# 100
  • 类属性,可以直接在类中定义的属性是类属性
  • 类属性可以通过类或类的实例访问
  • 但是类属性只能通过类对象来修改,无法通过实例对象来修改
实例属性
  • 通过实例对象添加的属性属于实力属性
  • 实例属性只能通过实例对象来访问和修改,类对象无法访问修改
class A(object):
	count=0
	def __init__(self):
		self.name='tom'
a=A()
a.name='jack'
print(a.name)
# jack
实例方法
  • 在类中定义,以self为第一个参数的方法都是实例方法
  • 实例方法在调用的时候,Python会将调用对象作为self传入
  • 实例方法可以通过实例和类调用,通过类调用时候不会传self,此时必须手动传递self
class A(object):
	count=0
	def __init__(self):
		self.name='tom'
	def test(self):
		print('test')
a=A()
a.test()
A.test(a)
# test
# test
类方法
  • 在类的内部,@classmethod 来修饰的方法属于类方法
  • 类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象
  • 类方法和实例方法的区别 实例方法的参数是self,而类方法的第一个参数是cls
  • 类方法可以通过类去调用,也可以通过实例调用,没有区别
class A(object):
	count=0
	def __init__(self):
		self.name='tom'
	@classmethod
	def test(cls):
		print('test')

A.test()
# test
静态方法
  • 在类中使用@staticmethod 来修饰的方法属于静态方法
  • 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例去调用
  • 静态方法,基本上是一个和当前类无关的方法,只是保存到当前类中的函数
  • 静态方法一般都是一些工具方法
class A(object):
	count=0
	def __init__(self):
		self.name='tom'
	@staticmethod
	def test():
		print('test')
a=A()
a.test()
A.test()
# test
# test
垃圾回收

在程序中没有被引用的对象就是垃圾,垃圾回收就是将垃圾对象从内存中进行删除(Python中有自动的垃圾回收机制)

class A(object):
	def __init__(self):
		self.name='tom'
a=A()
print(a.name)
a=None # 将a设置为None 此时没有任何变量使用A()对象进行引用,就是变成了垃圾 程序结束的时候也会触发垃圾回收
class A(object):
	def __init__(self):
		self.name='tom'
	def __del__(self): # 这是个魔术方法,当对象没有引用的时候会自动调用这个方法
		print('i was deleted')
a=A()
print(a.name)
a=None
# tom
# i was deleted
特殊方法(魔术方法)
  • 特殊方法都是双下划线开头和结尾的
  • 特殊方法一般不需要手动调用,在特殊的情况下自动执行
__str()__

可以指定对象转换为字符串的结果

class Person(object):
	def __init__(self,name,age):
		self.name=name
		self.age=age
    def __str__(self):
		return 'Person [name=%s,age=%d]'%(self.name,slef.age)
p1=Person('TOM',18)
p2=Person('jack',16)

#1 .print(p1) # 当我们在打印对象的时候,实际上是调用了对象中的特殊方法__str__
# <__main__.Person object at 0x7f08d587b410> 
# 2 .print(p1)# Person [name='tom',age=18]
__repr__

直接在交互模式中输出的结果

例如

>>> a='hello'
>>> print(a)
hello # 调用的str
>>> a
'hello' # 调用的repr
>>> 
__gt__

大于比较

class Person(object):
	def __init__(self,name,age):
		self.name=name
		self.age=age
	def __gt__(self,other):
		return self.age>other.age
p1=Person('TOM',18)
p2=Person('jack',16)

print(p1>p2)
# True
  • lt <
  • le <=
  • eq =
  • ne !=
  • gt >
  • ge >=
__len__

__bool__ 本来是判断对象是否为空

class Person(object):
	def __init__(self,name,age):
		self.name=name
		self.age=age
	def __gt__(self,other):
		return self.age>other.age
p1=Person('TOM',18)
p2=Person('jack',16)
p1=None
print(bool(p1)) # false

可以通过bool来指定对像转为布尔值的条件

还有许多的魔法方法都可以改比如加减乘除,都是多态的表现

模块
创建模块

模块化,指的是将一个完整的程序分解为一个一个小的模块,通过将模块组合,搭建出一个完整的程序

在Python中一个py文件就是一个模块,创建模块就是创建py文件

注意:模块名要符合合格的标识符的规范

如何在其它模块中引入外部模块

  1. import模块名 (模块名,就是Python文件的名字,不需要py后缀)

    • import test_module

    • 模块可以引入同一个模块多次,但是模块的实例只会创建一个

  2. import 模块名 as 模块别名

    • import test_module as test
    • print(test)
    • import 可以在程序的任意位置调用,一般都写在开头
    • 没一个模块都有一个__name__属性可以查看当前模块的文件名
    • 注意__name__属性为__main__的模块是主模块,一个程序中只会有一个主模块,就是通过python执行的模块
使用模块

访问模块中的属性:模块名.变量名

#bb.py
a=10
b=20
# aa.py
import bb
print(bb.a)
# 10

访问模块中的函数:模块名.函数名

访问模块中的类: 模块名.类

也可以引入模块中的部分内容

语法:from 模块名 import 变量,变量…

  • from m import Person,test
  • from m import * #一般不会轻易使用 会和主模块冲突

也可以为引入的变量使用别名

语法:from 模块名 import 变量 as 别名

  • from m import test2 as new_test2

添加了_的变量只能在模块内部访问,相当与私有变量,通过import *引入时不会看见 但import m 可以

*规定某些代码,只有当当前模块是主模块的时候才执行,而当前代码被其它模块引入的时候不需要执行

if __name__=='__main__':
    pass
包 package
  • 包也是一个模块,当我们模块中代码过多时,或者一个模块需要被分解成多个模块时,就需要使用到包

  • 普通的模块就是一个py文件,包就相当于一个文件夹

  • 包中必须要有一个__init__.py

from hello import a,b
print(a.xxx)
print(b.xxx)

包的好处就是可以把功能相似的代码放到一块

__pycache__

模块缓存文件,py在执行前,需要被解析器转为机器码,然后再执行,所以我们在使用模块(包)时,需要将模块的代码转为机器码然后再交由计算机执行,而为了提高程序运行性能,Python会在编译一次过后,将代码保存到一个缓存文件中,下次加载这个包时,不再重新编译,加载缓存缓存中的.

python标准库

开箱即用(在官方文档中global module index中可以找到全局的索引库)

为了实现开箱即用,Python中提供了一个模块的标准库,标准库是Python安装自带的

sys模块,提供了一些变量和函数,可以获取到Python的解析器

pprint模块,提供了我们一个方法pprint()该方法可以用来对打印的数据做简单的格式化

import sys
import pprint
print(sys.argv)# 获取在命令行执行这个py脚本的参数
print(sys.modules) # 返回所有引入的模块,是一个字典,key是模块名字 value是模块对象
pprint.pprint(sys.modules) # 返回pretty(漂亮)的print语句
pprint.pprint(sys.path) # 他是一个列表,列表中保存的是模块的搜索路径(这个是pprint的路径) 
print(sys.platform) #表示当前Python运行的操作系统环境
sys.exit('gg')# 用来退出程序

os模块,让我们对操作系统进行访问

import pprint
import os

pprint.pprint(os.environ)# 返回的是系统的环境变量是个字典
os.system('ls') # 可以直接执行Linux命令
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值