1、类与实例
(1)类与对象
类就是一个模板,里面有多个函数(也称为方法)和变量,每个函数实现了一些功能,而对象则是根据类模板创建的实例,通过实例对象可以执行类中的函数
#创建类
class Foo:
def bar(self): #类中的函数,第一个参数一定是self(当然,不一定是self这个词)
pass #函数功能, 这里省略
def hello(self,name):
print('I am %s' %name)
obj = Foo() #根据Foo类创建对象obj,创建对象的时候,要记得后面加括号
obj.Bar() #执行Bar方法
obj.Hello('july') #执行Hello方法
注意:新创建的类中第一个参数一定是一个指向对象自身的参数(一般都是用self指代,但是你也可以指定别的词),这里的self就相当于java中的this
# 创建类
class Foo:
# 这里我们可以创建一个类级别的变量
# 它不会随着由此类创建的变量而变化,其实就是全局变量
name = 'Jan'
def hello(july, name): # 我这里把self改成了july,
# 但是只要它作为第一参数的位置没变,它依旧是Foo Class的自我指代
print('you are %s' %july.name)
print('I am %s' %name)
print('\n')
# 根据Foo创建的对象
obj1 = Foo()
obj2 = Foo()
obj1.hello('August') #这里会输出“you are Jan I am August”
obj2.hello('July') #这里会输出“you are Jan I am July”
注意:全局变量与局部变量(函数内定义的变量),如果名称相同,局部变量就会对全局变量进行覆盖(这里有过编程经历的人都知道,这里就不再细说了)
(2)构造函数
创建对象的时候,用来初始化对象的,默认自动执行,完成一些初始化的动作
# 创建类
class Foo:
def __init__(self):#这就是构造函数,它的职责是在模型创建的初期,就完成一些动作
#简单的说就是,自定义的初始化步骤:
#同样,它需要self来指代本身这个class
self.name='Jan'
def hello(self, name):
print('you are %s' %self.name)
print('I am %s' %name)
print('\n')
#当你创建一个Foo类的时候,init会被自动跑一遍:
obj = Foo()
# 在我们的例子中,我们默认给self自己的name变量,赋值为’JAN‘
# 此刻,当我们调用Foo的hello()方法时,hello自己的name变量,被赋值为'July'
obj.hello('July')
2、访问限制
(1)私有变量的定义
有时候,我们要对类的数据变量进行透明化,也就是让外部无法访问类内部的变量,或是可以访问,但是不能更改,这就相当于java中的private、public和protect,但是Python中没有直接定义类型的规矩,而是通过书写规则来定义。
当你不想让类中的内部属性被外部访问的时候,可以把属性的名称前加上两个下划线__(看着像一个,其实是两个下划线哦),在Python中,如果实例的变量名以双下划线__开头,就变成了一个私有变量,只有内部可以访问,而外部不能访问(当然了,你可以通过调用实例的方法,而这个方法再调用这个私有变量,这样就可以访问了),下面看一个例子吧:
# 首先这个类中,定义了几个变量,但是都是共有变量,外部可以直接访问和改变
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def detail(self):
print(self.name)
print(self.age)
LiLei = Student('LiLei', 12)
LiLei.age = 20 #这里直接访问LiLei的年龄,然后将其由12改为了20
LiLei.detail() #这里直接输出“Lilei 20”
上面这个例子是一个public类性的变量,外部可以访问,可以更改
class Student:
def __init__(self, name, age):
self.__name = name #这里对变量进行了封装,添加下划线,变成了私有变量
self.__age = age
def detail(self):
print(self.__name)
print(self.__age)
LiLei = Student('LiLei', 12)
LiLei.__age = 20
LiLei.detail() #执行的话,你会发现依然会输出12,上面的赋值没有执行
其实,这里我有一个疑惑,那就是LiLei.__age = 20,既然没有被执行,那么为什么没有报错呢,结果很流畅:
如果有读者看到,可以在下面的评论中为我解惑,当然了,如果以后我找到答案啦,也会进行更新的
(2)私有变量的访问和修改
当然成为私有变量以后,不是说就不能访问和更改了,我们可以在类的内部设置函数,通过对象的函数来访问和修改:
class Student(object):
def get_name(self): #访问name私有变量
return self.__name
def get_age(self):
return self.__age #访问age私有变量
def set_age(self, age): #设置age的值
self.__age = age
3、面向对象的三大特性
(1)封装
封装本身是指:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别,封装就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
其实上面的私有变量设置就是一种封装,让你不能修改里面的值,也不能访问内部的值,要访问可以,只能通过调用我的对象接口,至于我内部是怎么返回的,怎么修改的,你都不知道,API接口大家都应该知道,那就是一种封装。
再不懂的话,可以想象电脑,电脑对很多人来说,都是一个黑盒子,我们知道怎么用,但是不知道它内部是怎么实现的,它怎么一按电源开关,就会显示画面呢?这其实就是一种封装,将各种功能组合封装成一个盒子——电脑,然后你可以各种按键(也就是调用内部的API接口)来实现你想要的功能,一个熟练的白领看起来相当熟悉电脑的操作,但是其实他可能对电脑内部的原理一无所知。
封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问权限来使用类的成员。
在不懂就直接看百度百科:封装是什么?
class Student:
# 假定我们初始化一个Student类的时候要做的就是,记录下每个学生的名字和年龄
def __init__(self, name, age):
self.name = name
self.age = age
# 至此,我们用self指代student本身,并用name和age存下了他们的年龄和名字
#此时,我们新建一个学生
obj1 = Student('July', 18)
print(obj1.name) # 直接调用obj1对象的name属性
print(obj1.age) # 直接调用obj1对象的age属性
obj2 = Student('Aug', 73)
print(obj2.name) # 直接调用obj2对象的name属性
print(obj2.age) # 直接调用obj2对象的age属性
上面的例子中,我们把一些数据信息和函数方法封装成一个学生对象,如果要访问其信息,只要调用该学生对象的属性或方法即可,其实封装是一种理论和概念性的东西,现在不理解也无所谓,因为其实大多数时间,你都在不自觉地运用封装理念
(2)继承
继承的概念,这里就不再多介绍了,不会的可以去百度,这里列举一下继承的注意事项:
下面举个例子:
# 我们首先创建一个学生类,这个类是所有学生的父类
class Student:
def __init__(self, name, age): #构造方法
self.name = name
self.age = age
def detail(self): #一个普通方法
print(self.name)
print(self.age)
# 然后,我们创建一个小学生类
class PrimaryStudent(Student) #因为是继承于学生类,所以我们写在括号内
# 这里我们可以不写构造函数,于是我们就是直接沿用Student类的构造函数
def qingchun(self): # 我们有一些新的独有的方法,会被叠加起来
print('我的青春,我做主!')
# 接下来,我们创建一个大学生类
class CollegeStudent(Student):
def __init__(self, name, age, gf): #改写一下构造函数,这样父类的构造方法会被覆盖掉
self.name = name
self.age = age
self.gf = gf
def CS_detail(self): #大学生类独特方法
print(self.gf)
obj1 = PrimaryStudent('小王', 7)
obj1.qingchun() # 独有的方法
obj1.detail() #继承与爸爸的方法
obj2 = CollegeStudent('王思聪', 29, '张雨馨')
obj2.detail()
obj2.CS_detail()
注意:通过上面,大家可以看到Python中,继承父类写法很简单,直接将父类写入括号内就可以了
class D(object):
def bar(self):
print('D.bar')
class C(D):
def bar(self):
print('C.bar')
class B(D):
pass
class A(B, C):
pass
a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
在上面的例子中,A继承来B和C,而B和C又继承了D,如果你要调用一种方法,那么B中没有,你要去C中寻找,还是D中,如果去D中寻找,那就是深度优先了(顺着B一路找到低,没有再去找C),如果是去C中寻找,那就是广度优先了(先是同级查找,没有,再往深一层查找)
现在都是广度优先!
(3)多态
Python不支持多态,并且也用不到多态,多态的概念是应用与Java和C#这一类强类型语言中的,而Python是若类型语言,从变量的定义中就可以看出来,它对于变量的声明和创建根本就没有指定变量类型,而是根据变量的赋值来定义的(你看多么随意啊,格式非常随意,对于类型管理比较松)
知乎上对于多态的理解:多态知多少
百度上这个介绍也不错:谈谈你对多态的理解
4、获取对象的信息
(1)获取对象的类型——type()方法
对于type()方法,之前已经讲过了,这里就不再多说了
type("1234") == type("abd") #输出为true,表明都是字符串
(2)类型是否包含
class A:
pass
class B(A):
pass
class C(B):
pass
k=A()
g=B()
y=C()
isinstance(y, C) #判断一个对象是否是某种类型(包括继承关系),回答是true
isinstance(y, B) #因为C是继承自B的,所以返回值依然是true
isinstance('a', str) #也可以判断类型,就是判断‘a’是否是str类型(其实就继承关系),答案当然是肯定的,所以返回值为true
(3)获取对象的所有属性和方法——dir()
使用dir()函数,它会返回一个包含字符串的list(里面列举了所有的属性和方法),下面举个例子:
print(dir("123"))
输出结果:
因为结果太长了,这里就不一一显示出来了。
注意:类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回字符串长度,而我们一般用的len()方法其实本质就是调用的__len__()方法,也就是说‘124’.__len__()与len(‘124’)是等价的
(4)操作对象的状态——getattr()、setattr()和hasattr()
hasattr(object,atr)——判断对象object是否具有属性atr(然后根据这个判断,你就可以做很多事情了)
getattr(object,atr)——获取对象object的属性atr的值(当然,前提你得知道这个对象有这个属性)
setattr(object,atr)——通过上面两步,你得知了对象的属性值,如果你觉得不满意,可以直接对其进行设置
#下面讲述一下这三个方法的使用实例,以后估计会经常用到
class MyObject:
def __init__(self):
self.x = 9 #定义一个普通变量
self.__y = 10 #定义一个私有变量
def power(self):
return self.x * self.x
obj = MyObject() #上面首先创建了一个方法和对象,然后可以用这三个方法对其进行查询和更改了
hasattr(obj, 'x') #有木有属性'x',我们可以看到上面的方法中,是有x属性的,所以返回值为true
hasattr(obj, '__y') #外部访问私有变量是行不通的,所以这里会返回False
hasattr(obj, 'y') # 有属性'y'吗? 答案是显然没有的
setattr(obj, 'y', 19) # 设置一个属性'y',既然没有我们就可以设置一个,后面的值就是y的值
getattr(obj, 'y') # 获取y的值,你会发现,它的返回结果是19,正好是你设置的值
getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404,这种情况还是经常发生的,因为很多时候,我们不知道它有没有这个属性
hasattr(obj, 'power') # 有属性方法'power'吗? 这里不仅可以查询属性变量,还有属性方法和函数
getattr(obj, 'power') # 获取属性方法'power',这里获得属性,不会直接告诉你方法内容,下面我会显示一些内容
fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
fn() # 调用fn()与调用obj.power()是一样的
获得属性方法power的结果输出:
注意:私有变量利用上面三个方法是访问不到的,你用hassttr()查询,即便有,也会显示False,后面的两个方法更加没有了,所以调用和改变私有变量的方法,只能通过该对象本身自带的函数方法实现
5、实例属性和类属性
啥也不说了,直接上实例:
class Student(object):
name = 'Student' #类属性
def __init__(self, name):
self.name = name #对象属性赋值,每个对象都会有不同的对象属性值,但是它们的类属性值都是一样的
s = Student() # 创建实例s
print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
s = Student('Bob')
print(s.name) #查询s对象的name属性,首先会显示s的属性:Bob
print(Student.name) #当然这并不是说类的name属性也被修改了,这里依然会显示:Student
del s.name #删除实例对象的name属性
print(s.name) #再次调用s.name,由于实力对象的name属性已经删除了,所以类的name属性就会显示出来:Student
s.score = 90 #给对象s绑定属性score,注意这里的score只是对象s的,而不是类的,即便再次创建一个类对象,也不会有score
print(s.score)
从上面的例子可以看出来:如果对象属性和类属性名是一样的话,那么调用的时候,类属性会被屏蔽掉(就好像继承类中的方法重载一样),当你删除对象属性以后,再次调用的时候,显示的依然会是类属性