面向对象

面向对象

核心是对象。

正式来说:

  • 对象是一个数据以及相关行为的集合
  • 面向对象是功能上指向建模对象

通过数据行为方式来描述交互对象的集合。

在python中,一切皆为对象。

面向对象的优缺点

  • 优点

    • 解决程序的拓展性
  • 缺点

    • 就是复杂度远高于面向过程。
    • 交互式解决问题,无法准确预测结果。

类就是类别、种类

对象就是特征和技能的统一体。

类则是这一系列对象的特征和技能的结合。

对于现实世界,先有个体(即对象),才有类别;但对于程序,必须先有类,然后才有对象。

面向对象编程

OPP(object oriented programming)

其是一种程序设计思想。OPP把对象作为程序的一个基本单元,一个对象就包含了数据和操作数据的函数。

在Python中,所有数据类型都可以视为对象,同时,我们也可以自定义对象。

自定义的对象的数据类型就是面向对象中类(class)的概念

Demo:

假如要处理我们的成绩。为了表示学生的成绩:

  • 面向过程的方式

    stu1={"name":"Tom","score":99}
    stu2={"name":"Jack","score":85}
    

利用函数实现

def find_score(stu):
    print(stu["name"],":",stu["score"])

如果是面向对象

常见的概念
##类的定义和使用

面向对象设计的思想,先抽象出类,再根据类创建实例。

###类的定义
#

class ClassName(object):
    """dosctring"""
class_statement

类的命名,大驼峰式

所谓大驼峰就是变量名称的单词的首字母大写

创建一个类

class MyFirstClass:
    pass

类的作用是一个模版,我们可以在创建实例的时候,把一些我们认为必须要在绑定的属性填写进去。这时就通过特殊的"__init__"

方法。在创建实例的时候,绑定相关的属性。比如前面的name,score。

class Student:
    school="ZUCC"
    def __init__(self,name,score):
        self.name=name
        self.score=score
        
stu1=Student("Tom",99)#实例化
print((stu1.name,stu1.score,stu1.school))

和普通函数相比,在类中定义方法时,第一个参数必须是self。除第一个参数外,其他的和普通函数没什么区别。

self代表的是实例,而非类。

__init__方法

  • 为对象初始化自己独有的特征
  • 该方法中可以有任意的代码,但是一定不可以有返回值。

数据封装

class Student:
	def __init__(self,name,score):
        self.name=name
        self.score=score
        
    def find_score(self):
        print(self.name,":",self.score)
        
stu1=Student("Tom",99)
stu1.find_score()

我们通过__init__()让stu1实例本身就拥有了相关数据,如果要访问这些数据,我们可以直接在student类的内部定义相关的函数来访问数据,以此“封装”数据。

这些封装数据的函数和Student类本身是关联起来的,他们被称之为方法。

class Point:
    pass

p1=Point()
p2=Point()

p1.x=5
p2.y=4

p2.x=3
p2.y=6

print(p1.x,p1.y)
print(p2.x,p2.y)
#通过点记法给一个实例化的对应赋予任何属性
#没有任何行为

#传参
class Point:
    def reset(self,x,y):
        self.x=x
        self.y=y
        
p=Point()
p.reset(4,5)
print(p.x,p.y)

#返回两个对象的距离
import math

class Point:
    def __init__(self,x,y):
        self.x=x
        self.y=y
        
    def move(self,x,y):
        self.x=x
        self.y=y
        
    def reset(self):
        self.move(0,0)
        
    def cal_distance(self,other_point):
        return math.sqrt(
        	(self.x-other_point.x)**2+
    		(self.y-other_point.y)**2)
    
p1=Point()
p2=Point()
p3=Point()


p1.reset()
p2.move(3,4)
p3.move(6,8)

print(p2.cal_distance(p1))
print(p2.cal_distance(p3))

类的两个作用

  • 属性引用

    • 类名、属性
  • 实例化

    • 类名加上一个括号就是实例化,它能够自动触发__init__函数的运行,进而为每个实例定制自己的特征

类属性的补充

类属性的查看

  • 1.dir(类名)

    • 返回一个列表
  • 2.类名:__dict__

    • 返回一个字典,key为属性名,value是属性值。

特殊的类属性

类名.__name__ #返回类的名字
类名.__doc__#类的文档字符串
类名.__base__#类的第一个父类
类名.__bases__#类的所有父类构成的元组
类名.__module__#类定义所在的模块
类名.__class__#实例所对应的类
类名.__dict__#类的字典属性

总结:

class ClassName:
    def __init__(self,para1,para2,...):
        self.对象属性1=para1
        self.对象属性2=para2
    def 方法名1(self):
        pass
    def 方法名2(self):
        pass
    
obj=ClassName(para1,para2)
#对象的实例化,代表具体的东西
#ClassName():调用__init__
#括号内传参,无需传入self,参数一一对应
#结果是返回对象obj

obj.对象属性1#查看对象的属性
obj.方法名1#调用类的方法

对象之间的交互

​ 假如说现在定义两个类 Person Dog

class Person:
    def __init__(self,name,aggressivity,life_value):
        self.name=name
        self.aggressivity=aggressivity
        self.life_value=life_value

    def attack(self,dog):
        dog.life_value-=self.aggressivity

class Dog:
    def __init__(self,name,breed,aggressivity,life_value):
        self.name=name
        self.breed=breed
        self.aggressivity=aggressivity
        self.life_value=life_value

    def bite(self,people):
        people.life_value-=self.aggressivity

per=Person("cjf",10,1000)
dog=Dog("rbk","Husky",8,1000)

print(dog.life_value)
per.attack(dog)
print(dog.life_value)

类命名空间与对象、实例的空间

创建一个类就会创建一个类的名称空间,用来存储我们定义的所有变量名,这些名字就是属性。

类的属性有两种:

  • 静态属性
    • 直接在类中定义的变量
  • 动态属性
    • 在类中定义的方法

静态属性是共享给所有对象的

动态属性是绑定到所有对象的

class Student:
    school="ZUCC"
    def __init__(self,name,score):
        self.name=name
        self.score=score
        
    def find_score(self):
        print(self.name,":","self.score")
        
stu1=Student("Tom",99)
stu2=Student("Jack",89)
print(id(stu1.school))
print(id(stu2.school))

print(stu1.find_score)
print(stu2.find_score)
print(Student.find_score)


12840448
12840448
<bound method Student.find_score of <__main__.Student object at 0x00D9CAD0>>
<bound method Student.find_score of <__main__.Student object at 0x00D9CAB0>>
<function Student.find_score at 0x00CDE8E8>

静态方法和类方法

1.静态方法

  • 通过装饰器@staticmethod来进行装饰。静态方法既不需要传递类对象也不需要传递实例对象
  • 静态方法也可以通过实例对象类对象去访问。
class Dog:
	type="狗"
	def __init__(self):
		name=None

	#静态方法
	@staticmethod
	def introduce():#静态方法不会自动传递实例对象和类对象
		print("犬科哺乳动物,属于肉食目")

dog=Dog()
Dog.introduce()
Dog.introduce()
'''
犬科哺乳动物,属于肉食目
犬科哺乳动物,属于肉食目
'''

静态方法是类中的函数,不需要实例。

静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但和类本身没有关系,也就是说,在静态方法中,不会涉及类中属性和方法的操作。

—>可以理解,静态方法是一个独立的,单纯的函数,仅仅是托管与某个类的名称空间中,便于维护和管理。

使用场景
  • 当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时,定义静态方法。
  • 取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗
  • 如果在类外面写一个同样的函数来做这些事,打乱了逻辑关系,导致代码维护困难,使用静态方法。

类方法

  • 类对象所拥有的方法
  • 需要用装饰器@classmethod,来标识其为类方法。
  • 对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数
class Dog:
	__type="狗"
	#类方法,用class来进行装饰
	@classmethod
	def get_type(cls):
		return cls.__type

print(Dog.get_type())
使用场景:
  • 当方法中需要使用类对象(如访问私有类属性等)时,定义类方法。
  • 类方法一般和类属性配合使用。
注意:

类中定义了同名的对象方法,类方法以及静态方法时,调用方法会优先执行最后定义的方法。

class Dog:
	def demo_method(self):
		print("对象方法")

	@classmethod
	def demo_method(cls):
		print("类方法")

	@staticmethod
	def demo_method():#最后被定义,调用时优先执行
		print("静态方法")

dog=Dog()
Dog.demo_method()
dog.demo_method()

'''
静态方法
静态方法
'''

property

概述

在Python中,主要为属性提供一个便利的操作方式

如果我们现在需要设计一个银行账户类,这个类中包含账户人的姓名,余额

class Account(object):
	def __init__(self,name,money):
		self.name=name
		self.money=money

问题:不安全(设计简单,外部可修改)

改进1 隐藏实现细节,考虑私有属性
	def __init__(self,name,money):
		self.__name=name
		self.__money=money

代码改进之后,将所有属性设计成私有属性后,确实从外部使用时,不知道内部的属性,不能直接修改对象,隐藏了实现的细节
但一个新的问题,如果确实需要对这两个属性进行修改,该怎么办?

改进2 提供一个精确的访问
class Account(object):
	def __init__(self,name,money):
		self.__name=name
		self.__money=money

	def get_name(self):
		return self.__name

	def set_balance(self,money):
		self.balance=money

	def get_balance(self):
		return self._balance

经过修改,外部使用这个类的对象时,想使用对象中的属性,只能通过类中提供的 set/get 接口来操作,提高了程序的安全性。

这样,程序基本达到了设计需求,但是能不能更加完善呢?

如果在使用这个类的对象过程中,由于误操作,传入了不正常的数据,导致数据异常。该如何以避免这种情况发生呢?

比如:设置金额时出现了负数,或字符串,或其它类型的对象。

改进3 保证数据的有效性

set_balance方法中,对传入数据进行有效性判断,如果是无效数据,提示用户出错。

class Account(object):
	def __init__(self,name,money):
		self.__name=name
		self.__money=money

	def get_name(self):
		return self.__name

	def set_balance(self,money):
		if isinstance(money,int):
			if money>0:
				self.__balance=money
			else:
				raise ValueError('输入金额不正确')
		else:
			raise ValueError('输入金额不是数据')

	def get_balance(self):
		return self.__balance

经过几版本的迭代,程序看上去越来越健壮,安全性也越来越高。

但是使用过程中,可不可以更精炼一些?

属性操作。

property类

在Python中,提供了一个property类,通过对创建这个类的对象的设置,在使用对象的私有属性时,可以不再使用属性的函数的调用方式,而是像普通的公有属性一样去使用属性,为开发者提供便利。

property(fget=None,fset=None,fdel=None,doc=None)#property attribute

property是一个类,__init__方法由四个参数组成,实例后返回一个用来操作属性的对象

  • 参数一:属性的获取方法
  • 参数二:属性的设置方法
  • 参数三:属性的删除方法
  • 参数四:属性描述

通过 property 类实例对象以后,在使用对象中的属性时,就可以像使用普通公有属性一样来调用,但是实际调用的还是 set/get 方法。 在实例 property 对象时,不是所有的参数都需要写,比如示例中的 name 只提供了 get 方法,并且是一个私有的方法。这样就完全隐藏了内部的实现细节 。

class Account(object):
	def __init__(self,name,money):
		self.__name=name
		self.__balance=money

	def __get_name(self):
		return self.__name

	def set_balance(self,money):
		if isinstance(money,int):
			if money>0:
				self.__balance=money
			else:
				raise ValueError('输入金额不正确')
		else:
			raise ValueError('输入金额不是数据')

	def get_balance(self):
		return self.__balance


#使用property类来为属性设置便利的访问方式
	name=property(__get_name)
	balance=property(get_balance,set_balance)

ac=Account("Tom",10000)
print(ac.name)
print(ac.balance)
ac.balance=1000
print(ac.balance)

使用@property

class Account(object):

	def __init__(self,name,money):
		self.__name=name
		self.__balance=money
	@property
	def name(self):
		return self.__name

	@property
	def balance(self):
		return self.__balance

	@balance.setter
	def balance(self,money):
		if isinstance(money,int):
			if money>0:
				self.__balance=money
			else:
				raise ValueError('输入金额不正确')
		else:
			raise ValueError('输入金额不是数据')

ac=Account("Tom",10000)
print(ac.name)
print(ac.balance)
ac.balance=1000
print(ac.balance)

self

如果对象的方法中需要使用该对象的属性,该怎么办?

  • 关键字self主要用于对象方法中,表示调用该方法的对象。
  • 在方法中使用self,可以获取到调用当前方法的对象,进而获取该对象的属性和方法。

调用对象的方法时,为什么不需要设置self对应的参数?

  • 某个对象调用其方法时,Python解释器会把这个对象作为第一个参数传递给方法,所以开发者只需要在定义的时候预留第一个参数self即可。
class Cat:
	#方法
	def introduce(self):
		print("name is: %s,age is: %d"%(self.name,self.age))

#实例化,创建一个对象
cat1=Cat()
cat1.name="大嘴"
cat1.age=6
cat1.introduce()

#创建另一个对象
cat2=Cat()
cat2.name="小鸾"
cat2.age=8
cat2.introduce()

方法内定义属性

  • 使用self操作属性和对象的变量名在效果上类似。如果属性在赋值时还没有被定义。就会自动定义一个属性并赋值。
class Cat:
	def introduce(self):
		self.type="小型动物"

cat=Cat()
cat.introduce()
print(cat.type)

__new__方法

  • 创建对象时,系统会自动调用__new__方法

  • 开发者可以使用__new__方法来自定义对象的创建过程

  • __new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

  • __new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例

  • __init__有一个参数self,就是这个__new__返回的实例,__init____new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

  • 如果创建对象时传递了自定义参数,且重写了new方法,则new也必须 “预留” 该形参,否则init方法将无法获取到该参数

class Cat:
	def __new__(cls,name):
		print("创建对象")
		return object.__new__(cls)

	def __init__(self,name):
		print("对象初始化")
		self.name=name

	def __str__(self):
		return "%s"%self.name

cat=Cat("蓝猫")
print(cat)
class A(object):
    def __new__(cls, x):
        print ('this is in A.__new__, and x is ', x)
        return super(A, cls).__new__(cls)

    def __init__(self, y):
        print ('this is in A.__init__, and y is ', y)

class C(object):
    def __new__(cls, n):
        print ('this is in C.__new__, and n is ', n)
        return super(C, cls).__new__(cls)

    def __init__(self, a):
        print ('this is in C.__init__, and a is ', a)


# class B(A):
#     def __new__(cls, z):
#         print ('this is in B.__new__, and z is ', z)
#         return A.__new__(cls, z)

#     def __init__(self, m):
#         print ('this is in B.__init__, and m is ', m)

class B(A):
    def __new__(cls, z):
        print ('this is in B.__new__, and z is ', z)
        return object.__new__(cls)
    def __init__(self, m):
        print ('this is ni B.__init__, and m is ', m)

if __name__ == '__main__':
    a = A(100)
    print ('=' * 20)
    b = B(200)
    print (type(b))

'''
this is in A.__new__, and x is  100
this is in A.__init__, and y is  100
====================
this is in B.__new__, and z is  200
this is ni B.__init__, and m is  200
'''

__call__方法

call()方法能够让类的实例对象,像函数一样被调用;

对象后面加括号,触发执行

构造方法的执行是有创建对象触发的,即:对象=类名()

而对于__call__方法的执行是由对象后加括号触发的,即对象()或者类()

class A(object):
    def __init__(self):
        pass
    
    def __call__(self,x):
        print('__call__ called, print x:',x)
        
a=A()
a('123')# 看a('123')这是函数的调用方法,这里a实际上是类对象A的实例对象,实例对象能想函数一样传参并被调用,就是__call__()方法的功能

img

isinstance和issubclass

isinstance(obj,cls)检查obj是否是类cls的对象

class Foo:
	pass

foo=Foo()
print(isinstance(foo,Foo))
print(isinstance(1,int))

issubclass(Bar,Foo)检查Bar是否是Foo的子类

class Foo:
	pass

class Bar(Foo):
	pass

print(issubclass(Bar,Foo))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值