python基础 | 面向对象,封装:定义类,类的继承(附:单个*双星号**,字典的update方法)

本文详细介绍了面向对象编程的概念,包括类和对象的区别、封装、类成员变量和方法的定义,以及继承、多态、构造函数、析构函数和权限管理。通过实例演示了如何使用这些概念在Python中创建和操作类,以及链式调用和字典操作的应用。
摘要由CSDN通过智能技术生成

1、面向对象

面向过程: 关注做事的步骤(对事),最直观的想法 ,中间插入东西变化会很大,不好改,所以大型项目驾驭不了

面向对象: 关注找出对象(对人),事情是由人相互的交互 去完成的。对象(人)的描述 分为静态的属性(高矮胖瘦) 和 动态的方法(跳舞)

以下棋为例:
面向过程: 1开始游戏 ->2黑子先走-> 3绘制画面 -> 4判断输赢 -> 5轮到白子 -> 6 绘制画面 -> 7判断 输赢 -> 8返回步骤2 -> 9 输出最后结果

面向对象:棋手 棋盘 裁判
变化由对象间交互完成: 黑方走哪个位置,白方如何应对
变中求不变: 事是人做的,事可变化万千,但人不变

2、封装:定义类

封装:把相关的信息,聚集在一起打包,方便重用,和信息隐藏

2.1 类与对象

在Python中,使⽤class关键字定义类,类是⼀个抽象描述,并不是真正的存在,抽象概念,指的是一类物体
需要把它初始化才会产⽣⼀个真正的事物(实例对象),我们称之为对象
在编程过程中,拥有⾏为(函数)和数据的是对象,⽽不是类

class Person:
    pass  # An empty block

p = Person()
print(p)

输出结果:

<__main__.Person object at 0x0000022256BD16D0>

创建了类Person的对象(构造函数,创建类实例),将其打印出来

class Person1:
    def sayHi(self):
        print('Hello, how are you?')


p1 = Person1()
p1.sayHi()

输出结果:
Hello, how are you?
成员函数创建对象调用

类的成员函数(方法)定义时 必须要传入self参数,类似于C++ this,指向实例对象的实体,即p1

调用的时候 默认前面第一个有个参数是其对象(一定要省,加了就报错了)类似于(错误代码)p1.sayHi(p1) (正确代码)p1.sayHi()

2.2 类成员变量

1、Python 中所有的类成员(包括数据成员)默认都是公有的,没有权限控制符
2、如果你使⽤的数据成员名称以双下划线前缀,⽐如 __privatevar,Python 的名称管理体系会有效地把它作为私有变量(约定俗成的规定)
3、这样就有⼀个惯例,如果某个变量只想在类或对象中使⽤,就应该以单下划线前缀。⽽其他的名称都将作为公共的,可以被其他类或者对象使⽤。记住这只是⼀个惯例,并不是Python所要求的(与双下划线前缀不同)

注意,在访问类内属性的时候,必须加 self,如:self.name

class Person2:
    def __init__(self, name):
        self.name = name

    def sayHi(self):
        print('Hello, my name is', self.name)


p2 = Person2('张三')
p2.sayHi()

输出结果:
Hello, my name is 张三
def __init__(self, name):类似于c++构造函数,对象创建的初始化
Person2('张三'):将字符串 张三 传进去

class Person3:
    # 统计人口数量
    population = 0

    # 类的初始化
    def __init__(self, name):
        self.name = name
        print('(Initializing %s)' % self.name)
        Person3.population += 1
    # 成员函数
    def sayHi(self):
        print('Hi, my name is %s.' % self.name)

    def howMany(self):
        if Person3.population == 1:
            print('I am the only person here.')
        else:
            print('We have %d persons here.' % Person3.population)


zs = Person3('张三')
zs.sayHi()
zs.howMany()

ls = Person3('李四')
ls.sayHi()
ls.howMany()

zs.sayHi()
zs.howMany()
print("\n\n")

输出结果:
(Initializing 张三)
Hi, my name is 张三.
I am the only person here.
(Initializing 李四)
Hi, my name is 李四.
We have 2 persons here.
Hi, my name is 张三.
We have 2 persons here.

class Actor:       # 定义类(只是想法,并不分配资源,分配资源要等实例化)
  name = '赵丽颖'  # 类属性
  age  = 35
  def act(self):   # 类行为(方法):形参this必须有,代表类的实例
    print(self.name," 会演戏")
  def sing(self):
    print(self.name," 会唱歌")

obj = Actor() # 创建类的实例对象 
print(obj.name) # 访问对象的属性
print(obj.age)
obj.act()     # 调用对象的方法(无参,但其实有参,它是隐藏的第一个实参,为类的实例)	
obj.sing()    

运行结果
运行结果

2.3 动态添加属性和方法

可以在之后使用时再加,边运行边改变,动态语言特点,逐行解析。C/C++静态语言 先编译后执行 而不是逐行编译执行,所以 定义好了之后就不能改了

class Actor:       # 定义类(只是想法)
  name = '赵丽颖'  # 类属性
  age  = 35
  def act(self):   # 类行为(方法):形参this必须有,代表类的实例
    print(self.name," 会演戏")
  def sing(self):
    print(self.name," 会唱歌")

obj = Actor() # 创建类的实例对象 

# 动态语言:可动态添加属性和方法(python是动态语言,不像c++静态语言,定义完后,后面不能修改)
obj.addr="成都"  # 动态添加属性
print(obj.addr)

def show(self):
    print(self.name,self.age,self.addr)

obj.show = show; # 动态添加方法(注意定义函数之后要加入这一句表示其是对象的方法)
obj.show(obj) # 动态添加的要传入对象势力,不能隐藏 之前会先实例化 actor() 分配空间把引用地址 放到obj里面
obj.sing() 	

运行结果:
运行结果

3、类的继承

描述种类繁多的对象间的关系,加括号 表示继承自哪一个父类

# 基类(父类)
class SchoolMember:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('(Initialized SchoolMember: %s)' % self.name)

    def tell(self):
        print('Name:"%s" Age:"%s"' % (self.name, self.age))

# 派生类(子类)可以直接调用父类的属性 和 方法,不用重复写父类的属性和方法
class Teacher(SchoolMember):
    def __init__(self, name, age, salary):
        SchoolMember.__init__(self, name, age)  # 先调用基类的
        self.salary = salary
        print('(Initialized Teacher: %s)' % self.name) # 调用父类的对象,使用自己的参数

    def tell(self): #派生类
        SchoolMember.tell(self)  # 调用基类的函数
        print('Salary: "%d"' % self.salary) # 调用自己的类中的成员变量


class Student(SchoolMember):
    def __init__(self, name, age, marks):
        SchoolMember.__init__(self, name, age)
        self.marks = marks
        print('(Initialized Student: %s)' % self.name) # 把父类的参数也继承了

    def tell(self):
        SchoolMember.tell(self) # 基类有自己的tell函数,调用基类的函数,加上自己的语句
        print('Marks: "%d"' % self.marks)


t = Teacher('张老师', 40, 30000)
s = Student('王二', 22, 75)
members = [t, s]  # 列表有两个成员
for member in members:
    member.tell()

输出结果:
(Initialized SchoolMember: 张老师)
(Initialized Teacher: 张老师)
(Initialized SchoolMember: 王二)
(Initialized Student: 王二)
Name:“张老师” Age:“40”
Salary: “30000”
Name:“王二” Age:“22”
Marks: “75”

3.1 定义父类

父类 通常是抽象出来的,语法上就是定义一个 普通的类:

class Animal:      # 定义类(只是想法)
  name = 'animal'  # 类属性
  def eat(self):   # 类行为(方法):形参self必须有(类似this),代表类的实例
    print(self.name," can eat")
  def breath(self):
    print(self.name," can breath")
  def run(self):
    print(self.name," can run")	

3.2 子类继承

继承:复用(不用重复写父类方法)
扩展:子类Cat在继承父类Animal的能力eat,run基础上,可派生新能力,如catchMouse
隐藏:子类会把父类的同名方法挡住,只要有同名(不管参数怎么样),就会调用那个函数,参数不匹配报错。先查找子类的方法,没有才沿着继承树回溯,查找其父类的方法

class Cat(Animal):    # 定义子类Cat继承于类Animal(父类)
   name='cat'
   def catchMouse(self):  # 派生的新方法
     print(self.name," can catchMouse")
   def breath(self):
     print(self.name," can breath from air")

c = Cat()
c.catchMouse()
c.run()		

运行结果:
运行结果

class Fish(Animal):    
   name='fish'
   def swim(self):  # 派生的新方法
     print(self.name," can swim")
   def breath(self,str):  # 隐藏(遮挡 父类的同名函数)
     print(self.name," can breath from ",str)	

f = Fish()
f.eat()
f.swim()
#f.breath() 报错,参数部匹配
f.breath('water') # 参数要匹配,只要出现同名他就调用了

运行结果:
运行结果
不支持自动匹配不同参数的同名函数(在C++里面是支持的),事实上定义 同名的函数它只认最后一个,调用前面一个对应的参数 会报错

class Fish(Animal):    
   name='fish'
   def swim(self):  # 派生的新方法
     print(self.name," can swim")
   def breath(self,str):  # 隐藏(遮挡 父类的同名函数)
     print(self.name," can breath from ",str)
   def breath(self):
     print(self.name,"second breath")

f = Fish()
f.eat()
f.swim()
#f.breath() # 报错,参数部匹配
f.breath('water') # 这个都无法顺利执行

报错:
报错

class Fish(Animal):    
   name='fish'
   def swim(self):  # 派生的新方法
     print(self.name," can swim")
   def breath(self,str):  # 隐藏(遮挡 父类的同名函数)
     print(self.name," can breath from ",str)
   def breath(self):
     print(self.name,"second breath")

f = Fish()
f.eat()
f.swim()
f.breath() # 跟最后一个匹配

运行结果:
运行结果

f.breath();   # 会报错参数个数不匹配,因子类会隐藏了父类的同名函数, 
              # 1.用默认参数解决
              # def breath(self,str='water')  
	          # 	print(self.name," can breath from ",str)	
	          # 注意:用重载不行,下面的同名函数会覆盖上面的同名函数,不会智能匹配参数个数
              # 2.用不定参   

3.3 多重继承

class Father:
  name ="father"
  def football(self):
    print(self.name,"play football good")
  def talk(self):
    print(self.name, "talk fast")

class Mother:
  name ="mother"
  def music(self):
    print(self.name,"play music good")
  def talk(self):
    print(self.name, "talk slow")

class Son(Father,Mother): # 多重继承:儿子同时继承父母天赋
  name ="son"
  def draw(self):
    print(self.name,"draw good")

s =Son()
s.football()
s.music()
s.draw()
s.talk()  # python没有二义性问题(c++有),父母都有时,以前一个为准

编程思想的角度 尽量避免,增加了耦合性,事实上 继承都不要乱用(雁群和大雁的关系不是继承,临时的关系 和 固有的关系),继承滥用 会产生类的爆炸

3.4 多态

python 天生多态(动态语言,运行时动态绑定)根据传进来的子类的类型,自动选择对应子类的函数

class Animal:      # 定义类(只是想法)
  name = 'animal'  # 类属性
  def eat(self):   # 类行为(方法):形参self必须有(类似this),代表类的实例
    print(self.name," can eat")
  def breath(self):
    print(self.name," can breath")
  def run(self):
    print(self.name," can run")	

class Cat(Animal):    # 定义子类Cat继承于类Animal(父类)
   name='cat'
   def catchMouse(self):  # 派生的新方法
     print(self.name," can catchMouse")
   def breath(self):
     print(self.name," can breath from air")
   def run(self):
     print(self.name, "run on the ground")

class Fish(Animal):    
   name='fish'
   def swim(self):  # 派生的新方法
     print(self.name," can swim")
   def breath(self,str):  # 隐藏(遮挡 父类的同名函数)
     print(self.name," can breath from ",str)	
   def run(self):
     print(self.name, "run in the water")

def sport(obj):
  obj.run()
c = Cat()
f = Fish()
sport(c)
sport(f)

运行结果:
运行结果

3.5 构造函数和析构函数

构造函数:类实例化对象的时候,如果没有自己定义 __init__ (self)就会有一个默认构造函数(一旦定义就不会调用默认构造函数了),反正 会调用构造函数

class Line:
    name = 'a line'
    def __init__(self, len=3):
        self.length = len
        print("调用了构造函数")
    def show(self):
        print(self.name, "show", self.length)

l = Line()

运行结果:
运行结果
析构函数:销毁类,把资源还回去,系统有默认的,也可以自己定义 __del__(self)

obj = Line() # 创建对象实例时,自动调用构造函数。
obj.show();  # 执行完自动析构(引用计数方式,进行垃圾回收)

运行完了就算报了错,还是执行析构函数的

class Line:
    name = 'a line'
    def __init__(self, len):
        self.length = len
        print("调用了构造函数")
    def __del__(self):
        print("析构函数调用,释放资源")
    def show(self):
        print(self.name, "show", self.length)

l = Line()

报错:
报错信息
完整过程

class Line:
    name = 'a line'
    def __init__(self, len = 3):
        self.length = len
        print("调用了构造函数")
    def __del__(self):
        print("析构函数调用,释放资源")
    def show(self):
        print(self.name, "show", self.length)

l = Line(9)
l.show()

l2 = Line()
l2.show()

运行结果:
运行结果
退出的时候把前面的两个对象都销毁了,释放资源(调用析构函数)

3.6 权限

之前所有的类成员 都是在哪都可以访问,设置私有属性,变量前面加__
可以通过 非私有的类方法 访问私有属性

但是其实python天生全开放,加__只是在语法解析的时候 避免修改,C++严格的安全机制,编成机器码,源码是看不到的

class Girl:      # 定义类(只是想法)
  name = 'lili'
  __age = 30     # 私有属性(以下划线开头)
  def registerInfo(self):
    print(self.name,'age is ',self.__age)  # 私有属性仅内部能访问
  def __getAge(self): # 私有方法(以下划线开头)
    print('private __getAge is ',self.__age)

g = Girl()
g.registerInfo()    # 通过非私有方法访问 私有属性
print(g.name)
# g.__getAge()        # 报错: 私有方法外部不能访问 
# print(g.__age)      # 报错: 私有属性外部不能访问   
print(g._Girl__age) # 通过这种方式,外面也能够访问“私有”变量;调试中是比较有用的:类前加_私有变量前加__

运行结果:
运行结果

3.7 链式调用

能够链式调用的前提是 链上的函数的返回值都要是 对象本身(return self
本质上 还是引用的替换

lass Person:
  def name(self, str):
    self.name = str
    return self
  def age(self, str):
    self.age = str
    return self
  def show(self):
    print(self.name,self.age)

obj=Person()
obj.name("ashergu").age(23).show()	
"""
等价于
obj.name("ashergu")
obj.age(23)
obj.show()
"""

例子:用链式调用,实现多关键词的信息查询

class Query():
    def __init__(self):
        self.query_condition = {}
    def filter(self, **kwargs):
        self.query_condition.update(kwargs)
        return self
				
query = Query()
a = query.filter(name='lili').filter(age__gt=18, address='chengdu').filter(salary=12000) #键值不需要加",用等号不用冒号
print(query.query_condition) #//最后提交给数据库的查询信息

附:单个星号*和双星号**不同的作用

单个星号*用于解包可迭代对象,例如元组或列表。它允许将可迭代对象拆分为单独的元素

双星号**用于解包字典。它允许将字典中的键值对拆分为单独的关键字参数和对应的值
上面的代码中,使用**kwargs时,可以将任意数量的关键字参数传递给函数,并在函数内部将它们作为字典进行处理

def example_function(**kwargs):
    for key, value in kwargs.items():
        print(f"Key: {key}, Value: {value}")

# 调用函数并传递多个关键字参数
example_function(name="Alice", age=30, city="New York")

附:字典的update方法

update 是字典对象的一个方法,用于 将一个字典的键值对更新到另一个字典中。在之前提供的代码中,self.query_condition是一个字典对象,而kwargs也是一个字典对象,update 方法将 kwargs 中的键值对更新到 self.query_condition 字典中

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值