类体中、所有函数之外:此范围定义的变量,称为类属性或类变量
类体中,所有函数内部:以“self.变量名”的方式定义的变量,称为实例属性或实例变量
类体中,所有函数内部:以“变量名=变量值”的方式定义的变量,称为局部变量
类变量:在类中,但在各个类方法外定义的变量
class test :
name = "you" # 定义类变量
age = "20"
def say(self, content): # 定义say实例方法
print(content)
name 和 age 属于类变量
类变量的特点:所有类的实例化对象都同时共享类变量,类变量在所有实例化对象中是作为公用资源存在的
类方法的调用方式:使用类名直接调用,使用类的实例化对象调用
print(test.name) #使用类名直接调用
test.name = "youchanwill" #修改类变量的值
print(test.name)
类名不仅可以调用类变量,也可以修改值
类变量为所有实例化对象共有,通过类名修改类变量的值,会影响所有的实例化对象
除了通过类名访问类变量,还可以动态地为类和对象添加类变量
通过类对象是无法修改类变量的,其本质将不再是修改类变量的值,而是在给该对象定义新的实例变量
除了通过类名访问类变量之外,还可以动态地为类和对象添加类变量
TEST = test()
TEST.catalog = 13
print(TEST.catalog)
实例变量:在任意类方法内部,以“self.变量名”的方式定义的变量
只作用于调用方法的对象,实例变量只能通过对象名访问,无法通过类名访问
class test :
def __init__(self):
self.name = "you"
self.age = "20"
def say(self): # 定义say实例方法
self.catalog = 7
name、age以及 catalog 都是实例变量
__init__() 函数在创建类对象时会自动调用,而say()方法需要类对象手动调用
test类的类对象都会包含name和age实例变量,只有调用了say()方法的类对象,才包含catalog实例变量
TEST = test() #只有调用say(),才会拥有catalog实例变量
TEST.say()
print(TEST.catalog)
通过类对象可以访问类变量,但无法修改类变量的值
通过类对象修改类变量的值,不是给类变量赋值,而是定义新的实例变量
Python只支持为特定的对象添加实例变量
TEST.money = 30
print(TEST.money)
局部变量以“变量名=值”的方式进行定义
class test :
def count(self,money): # 定义say实例方法
sale = 0.8*money
print("优惠后的价格为:",sale)
TEST= test()
TEST.count(100)
优惠后的价格为: 80.0
定义局部变量是为了所在类方法功能的实现,局部变量只能用于所在函数中,函数执行完成后,局部变量也会被销毁
关于Python的函数装饰器的内容,见之前文章
Python学习笔记(十):函数装饰器
property()
在不破坏类封装原则的前提下,依旧使用“类对象.属性”的方式操作类中的属性
属性名=property(fget=None, fset=None, fdel=None, doc=None)
fget参数:指定获取该属性值的类方法
fset参数:指定设置该属性值的方法
fdel参数:指定删除该属性值的方法,最后的doc是文档字符串,说明此函数的作用
property() 函数中参数的指定并不是完全随意的,可以仅指定第1个、或前2个、或前3个,也可以全部指定
class test:
def __init__(self,n): #构造函数
self.__name = n
def setname(self,n): #设置 name 属性值的函数
self.__name = n
def getname(self): #访问nema属性值的函数
return self.__name
def delname(self): #删除name属性值的函数
self.__name="xxx"
name = property(getname, setname, delname, '说明文档') #为name 属性配置 property() 函数
#说明文档,print(test.name.__doc__)
help(test.name)
TEST = test("you")
print(TEST.name) #调用 getname() 方法
TEST.name="Python教程"#调用 setname() 方法
print(TEST.name)
del TEST.name #调用 delname() 方法
print(TEST.name)
getname()方法中需要返回 name 属性,如果使用 self.name 的话,其本身又被调用 getname(),会进入死循环
name属性必须设置为私有属性,即__name
name = property(getname, setname)
name是一个可读写的属性,但不能删除,因为函数中并没有为name配置用于函数该属性的方法
test类中设计有 delname()函数,这种情况下也不能用来删除 name 属性
有关类属性和类方法的属性设置(共有属性、保护属性、私有属性)
name = property(getname) # name 属性可读,不可写,也不能删除
name = property(getname, setname,delname) #name属性可读、可写、也可删除,没有说明文档
封装(Encapsulation)
在设计类时,将一些属性和方法隐藏在类的内部,在使用此类时,无法直接以“类对象.属性名”(“类对象.方法名(参数)”)的形式调用
封装机制保证了类内部数据结构的完整性,避免了外部对内部数据的影响,提高了程序的可维护性
实现类的封装,Python采取下面的方法
默认情况下,Python类中的变量和方法都是公有(public)的,名称前没有下划线(_)
变量(函数)为私有变量类中的变量和函数,名称以双下划线“__”开头,属性等同于 private
可以定义以单下划线“_”开头的类属性或者类方法,这种类属性和类方法通常被视为私有属性和私有方法,也能通过类对象正常访问,但这是约定俗称的用法
以双下划线开头和结尾的类方法(例如类的构造函数__init__(self)),都是 Python内部定义的,用于Python内部调用,自己定义类属性或者类方法时,不要使用这种格式
class test :
def setname(self, name):
if len(name) < 3:
raise ValueError('名称长度过短') #有关raise的具体用法,简单理解成如果用户输入不规范,程序会报错
self.__name = name
def getname(self):
return self.__name
name = property(getname, setname) #为name配置setter和getter方法
def setadd(self, add):
if add.startswith("http://"):
self.__add = add
else:
raise ValueError('以http://开头')
def getadd(self):
return self.__add
add = property(getadd, setadd) #为add配置setter和getter方法
def __display(self):
print(self.__name,self.__add) #定义私有方法
TEST = test()
TEST.name = "you"
TEST.add = "http://youchanwill.com"
print(TEST.name)
print(TEST.add)
you
http://youchanwill.com
将name和add属性隐藏了起来,同时也提供了各自的setter和getter方法,这些方法都是公有(public)的
仅能通过暴露的setter()和getter()方法操作name和add属性,可以避免用户对类中属性的不合理操作,提高了类的可维护性和安全性
继承机制
用于创建和现有类功能类似的新类,通过继承机制,可以轻松实现类的重复使用
class Shape:
def draw(self,content):
print("画",content)
class Form:
def draw(self,content):
print("画",content)
def area(self):
#....
print("此图形的面积为...")
Shape类的draw()方法可以画出指定的形状,现在需要创建一个Form类,还可以计算出所画形状的面积
class Shape:
def draw(self,content):
print("画",content)
class Form(Shape):
def area(self):
#....
print("此图形的面积为...")
class From(Shape) 就表示 From 继承 Shape
类的继承机制:让From类继承Shape类,当From类对象调用draw()方法时,解释器会先去From中找以draw为名的方法,如果找不到,还会自动去Shape 类中找
Python 中,实现继承的类称为子类,被继承的类称为父类(基类、超类)
子类继承父类,在定义子类时,将父类放在子类之后的圆括号里
class 类名(父类1, 父类2, ...):
#类定义部分
如果该类没有显式指定继承自哪个类,则默认继承 object 类(object类是 Python中所有类的父类,直接父类、间接父类)
Python的继承是多继承机制,一个子类可以同时拥有多个直接父类
继承相对子类来说的,子类继承自父类;派生相对于父类来说的,父类派生出子类
子类拥有父类所有的属性和方法,就算属性或方法是私有(private)的
多继承需要面临的问题是,多个父类中包含同名的类方法
处置措施:根据子类继承多个父类时这些父类的前后次序决定,在前面父类中的类方法会覆盖排在后面父类中的同名类方法
class People:
def __init__(self):
self.name = People
def say(self):
print("People类",self.name)
class Animal:
def __init__(self):
self.name = Animal
def say(self):
print("Animal类",self.name)
class Person(People, Animal): #People中的name属性和say()会遮蔽Animal 类中的name属性
pass
test = Person()
test.name = "you"
test.say()
People类 you
当Person同时继承People类和Animal类时,People类在前,如果拥有同名的类方法,实际调用的是People类中的
虽然Python在语法上支持多继承,但多继承容易让代码逻辑复杂、思路混乱