一. 类和对象的概念
概念:
面向对象的两个最重要的概念:类和对象
类:共性事物的抽象,是对某一类具有共性事物的描述,是概念上的定义
对象:对象是共性事物的一个体现,是这类事物的每个个体,或者说是类的一个实例(instance)
总结:
类是对象的模板,对象是类的实例
类结构:
里面包含属性与函数
数据是对象的状态-->成员变量(属性)
方法是对象的行为-->函数(方法)
二. 语法
#创造类的语法 class Math: a = 4 #属性 b = 5 def add(self): #方法 c = self.a + self.b return c
注意:1) 类名一般首字母大写,比如 class User #class是关键字
2) 类里面包含属性和方法
3) 类函数自带self关键字(指向类的实例)。不能少!
4) 如果类函数里面要调用属性,方法: self.属性名, self.方法名
实例:
class Friend: height = None weight = None age = None money = None #定义功能——会做饭 def canCook(self, can=True): if can == True: print("我会做饭。。。") else: print("我不会做饭。。。") def canGetMoney(self): print("我会挣钱。。。") def setAge(self, age): self.age = age print("年龄为:", self.age) def setHeight(self, height): self.height = height print("身高为:", self.height) def setWeight(self, weight): self.weight = weight print("体重为:", self.weight) def setMoney(self, money): self.money = money print("资产为:", self.money) #对象一:身高180,会做饭,做挣钱,有200万的资产,而且才29岁 #实例化:类名() danqing = Friend() danqing.setAge(29) print(danqing.age) danqing.setMoney(2000000) print(danqing.money) danqing.canCook() print("===========================") #对象二:身高175,不会做饭,年龄只有25岁,资产5万 dancf = Friend() #改变对象的属性 dancf.setAge(25) print(dancf.age) dancf.setMoney(50000) print(dancf.money) dancf.setHeight(175) print(dancf.height) dancf.canCook(False)
运行结果:
年龄为: 29 29 资产为: 2000000 2000000 我会做饭。。。 =========================== 年龄为: 25 25 资产为: 50000 50000 身高为: 175 175 我不会做饭。。。
三. 初始化函数的调用
初始化函数:def __init__(self, 参数1, 参数2, 参数3)
__init__函数,是两个下划线!经常会写错,写成单下划线,这样会报错!
用法:
def __init__(self, a, b): self.a = a self.b = b self.c = 10
注意:
1) 初始化里面做的是初始化操作,可以带参数也可以不带参数
2) 跟普通函数一样,可以带默认参数
3) 初始化里面可以有赋值好了的属性值
4) 每次创造一个实例,需要传递跟初始化函数参数个数一致的值
5) 每个实例都会自动调用初始化函数
6) 初始化参数的写法要注意,怎么把参数赋值给self.参数名,参数名字不一定要一致,但是赋值要正确
实例1:
class Friend: def __init__(self, age, sex, height): self.age = age self.sex = sex self.height = height #定义功能——会做饭 def canCook(self, can=True): if can == True: print("我会做饭。。。") else: print("我不会做饭。。。") def canGetMoney(self): print("我会挣钱。。。")
xiaozhai = Friend(26, "男", 178) jian = Friend(18, "女", 155)
实例2:类也可以没有初始化
class Friend: #定义功能——会做饭 def canCook(self, can=True): if can == True: print("我会做饭。。。") else: print("我不会做饭。。。") def canGetMoney(self): print("我会挣钱。。。") def setAge(self, age): self.age = age print("年龄为:", self.age) def setHeight(self, height): self.height = height print("身高为:", self.height) def setWeight(self, weight): self.weight = weight print("体重为:", self.weight) def setMoney(self, money): self.money = money print("资产为:", self.money) jian = Friend() print(jian.age) 运行结果: Traceback (most recent call last): File "D:/python_workshop/python6/revise/类和对象(一).py", line 63, in <module> print(jian.age) AttributeError: 'Friend' object has no attribute 'age'
当直接调用函数后,再打印属性发现有age了,因为函数setAge里生成了一个全局属性age
jian = Friend() jian.setAge(25) print(jian.age) 运行结果: 年龄为: 25 25
四. 类的继承
1. 子类拥有父类的所有属性和行为
2. 子类可以扩展自己的属性和行为
3. 父类的行为不够用,子类要升级和优化,子类可重写父类的方法——多态
4. 子类和父类都有的行为:子类实例优先使用自己的,没有再去用父类的
支持多继承
多继承语法:用逗号隔开父类
class 子类类名(父类1, 父类2):
class Son(Father, Mother): def __init__(): XXXX
实例
class Father: def __init__(self, id, name, sex, age): self.id = id self.name = name self.sex = sex self.age = age def eat(self, food): print("eat:", food) def earnMoney(self, money): print("Father earn money:", money) class Mother: def sing(self): print("xxxxxx") def earnMoney(self, money): print("Mother earn money:", money) #子类继承了两个类的所有属性和方法 class Son(Father, Mother): #方法重写,调用该方法时会调用自己的方法 def eat(self, food): print("eat:", food) print("我吃的更快,比father更快!!!") def dance(self): print("xxxxxx") #子类没有定义的初始化方法,但是父类定义了初始化方法 #则子类实例化时会调用父类的初始化方法 jian = Son(123, "jian", "male", 22) jian.eat("apple") #当子类继承了多个类中同样的方法时,子类会优先使用第一个继承类的方法(从左向右) jian.earnMoney(2000) #父类实例化 jianBB = Father(111, "li", "male", 56) jianBB.eat("orange")
运行结果
eat: apple 我吃的更快,比father更快!!! Father earn money: 2000 eat: orange
继承—super用法
在子类的行为中,想调用父类的行为,然后再做额外扩展,可以使用super类
语法:super().行为(参数)
class Son(Friend): def __init__(self, age, sex, name): super().__init__(age, sex) #Friend.__init__(age, sex) 这样也可以 self.name = name
五. 封装数据
实现方式:遵循一定的属性和方法命名规约
不希望这些变量或者方法被外部使用
• 任何以单下划线_开头的名字都应该是内部实现
即不希望通过实例名字,变量名/方法名来调用,但是python并不会真的阻止别人访问内部名称,子类也可以继承并重写父类的方法,只是一种约定
• 以双下划线__开头的名字,仅类自己可访问
继承——这种方法通过继承是无法被覆盖(重写)的。其实也是可以访问的,只不过换成了_类名__变量名/函数名
class MyClass: def __init__(self): self._private_data1 = "私有方式一" self.__private_data1 = "私有方式二" pass def _pri_func(self): print("私有方法一") def __pri_func(self): print("私有方法二") def public_func(self ): print("公开方法") #类内部调用自己的私有方法一 self._pri_func() mc = MyClass() #仍然可以调用单下划线的属性和方法,只不过作为一种约定,告诉别人请不要这样调用 mc._pri_func() mc._private_data1 #mc.__private_data2 双下划线的方法无法调用 class SonClass(MyClass): #子类继承父类的单下划线私有方法并重写,也是可以的 def _pri_func(self): print("我是子类的私有方法") #子类无法继承父类的双下划线私有方法并重写,运行时报错 def __pri_func(self): print("我是子类的私有方法二") son = SonClass() son.__pri_func()
运行结果
Traceback (most recent call last): File "D:/python_workshop/python6/revise/私有属性和方法.py", line 37, in <module> son.__pri_func() AttributeError: 'SonClass' object has no attribute '__pri_func' 私有方法一
哪种方式好
两种不同的编码约定(单下划线和双下划线)来命名私有属性,问题就来了:到底哪种方式好呢?
大多数而言,你应该让你的非公共名称以单下划线开头,但是,如果你清楚你的代码会涉及到子类,并且有些内部属性应该在子类中隐藏起来,那么才考虑使用双下划线方案
六. @property和@属性名.setter
实例属性,除了访问和修改之后,增加其他逻辑处理,如合法性等
@property #获取属性值:实例.属性名 def age(self): return self._age @age,setter #给属性赋值时添加了逻辑处理 def age(self.age): if age in range(0, 121 ): self._age = age else: print("输入的年龄不符合要求哦!!")
实例
class People: #age是people类的属性名 @property def age(self): return self._age @age.setter def age(self, age): if age in range(0, 121): self._age = age print(self._age) else: print("年龄不符合要求") p = People() p.age = 134
运行结果:
年龄不符合要求
注意:
不能写成return self.age和self.age = age,原因如下
class People: ''' ========================================== @property和@属性名.setter可以用来对属性的值进行一定的逻辑处理。 但是: @property和@属性名.setter的小坑 =========================================== 在以下代码中,setter函数表示:当你使用self.age时就会自动的去调用setter 所以在@age.setter对应的函数中,else操作中,有使用self.age。 所以,这里的self.age相当于又去调用了setter,即又去调用了自己。就进入了一直在调用自己的状态。 于是,就会报错:递归次数超限制。 这个是个坑!!! 如何解决这个问题呢?如果设置可以爬出这个坑呢?? @property def age(self): return self.age @age.setter def age(self,age): if age not in range(0,121): print("age is not valid data") else: self.age = age ============================================ ================以下是爬坑策略==================== 为了不要重复的来调用自己,也就是在@age.setter对应的函数当中,不要使用self.age 所以用了另外一个变量名:self._age。这样子就不会一直调用。 ====代码不同之处 一:@property对应的函数中,return self._age ======= ====代码不同之处 二:@age.setter对应的函数中,else:self._age = age ======= 对外部使用者而言,还是age属性,即可以通过实例名.age来设置值。 对类内部而言,就是_age属性了。 以下是代码部分 ''' def __init__(self,name,sex): self.name = name self.sex = sex @property def age(self): return self._age @age.setter def age(self,age): if age not in range(0,121): print("age is not valid data") else: self._age = age p = People("jian","female") p.age = 100 print(p.age) p.age = 122 print(p.age)