类和实例
面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
通过class关键字定义People类
class People(object):
pass
class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
实例化
xiaoC =People()
print(xiaoC)
print(People)
#<__main__.People object at 0x000001E58945F280>
#<class '__main__.People'>
绑定属性
给类绑定一个属性,实例化对象就可以获取对应属性
People.name="xiaoC学安全"
print(xiaoC.name)
把认为必须绑定的属性预先填写到类中
class People(object):
def __int__(self,name,age):
self.name=name
self.age=age
注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
class People(object):
def __init__(self,name,age):
self.name=name
self.age=age
# People.name="xiaoC学安全"
xiaoC =People("xiaoC学安全",18)
print(xiaoC.name,xiaoC.age)
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
封装方法
在类的内部定义访问数据的函数,称为“类的方法”
class People(object):
def __init__(self,name,age):
self.name=name
self.age=age
def print_out(self):
print(f"{self.name}的年龄是{self.age}")
xiaoC =People("xiaoC学安全",18) #实例化一个对象
xiaoC.print_out() #调用类方法
#xiaoC学安全的年龄是18
访问和限制
在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。
但是现在外部代码还是可以修改类内部的数据,如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
class People1(object):
def __init__(self,name,age):
self.__name=name
self.__age=age
def print_out(self):
print(f"{self.__name}的年龄是{self.__age}")
xiaoA =People1("xiaoA学安全",18)
xiaoA.print_out()
print(xiaoA.__name)
如果需要修改只允许内部访问的属性,可以定义一个类方法,例如set_name()
class People1(object):
def __init__(self,name,age):
self.__name=name
self.__age=age
def print_out(self):
print(f"{self.__name}的年龄是{self.__age}")
def set_name(self,name):
self.__name=name
xiaoA =People1("xiaoA学安全",18)
xiaoA.print_out()
xiaoA.set_name("xiaoC学安全")
xiaoA.print_out()
需要注意的是,在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。
有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量:print(xiaoA._People1__name)
但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name改成不同的变量名。
总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。
继承和多态
当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。
class Run(object):
def run(self):
print('小C正在跑步')
class People(Run):
pass
XIAOC =People()
XIAOC.run()
当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。
class Animal:
def say(self):
print("调用的是 Animal 类的say方法")
class Plant(Animal):
def say(self):
print("调用的是 Plant 类的say方法")
class People(Animal):
def say(self):
print("调用的是 People类的say方法")
a = Animal()
a.say()
# 调用的是 Animal 类的say方法
a = Plant()
a.say()
# 调用的是 Plant 类的say方法
a = People()
a.say()
# 调用的是 People类的say方法
Plant类和People都继承Animal类,且各自都重写了父类的 say() 方法。从运行结果可以看出,同一变量 a 在执行同一个 say() 方法时,由于 a 实际表示不同的类的实例对象,因此 a.say() 调用的并不是同一个类中的 say() 方法,这就是多态。
多态可以简单理解为:具有多种形态 ,它指的是即便不知道一个变量所引用的对象到底是什么类型, 仍然可以通过这个变量调用方法; 在运行过程中根据变量所引用的对象的类型 , 动态的决定调用哪个对象中的方法。
判断对象属性
class Run(object):
def __init__(self,name):
self.name=name
def run(self):
print(f'{self.name}正在跑步')
xiaoc =Run("xiaoc")
print(isinstance(xiaoc,Run))
#True 判断xiaoc的类型是不是Run
print(dir(xiaoc))
#获得一个对象的所有属性和方法
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'run']
#判断对象的属性 True
print(hasattr(xiaoc,"name"))
#getattr可以获取对象的属性值
print(getattr(xiaoc,"name"))
#试图获取不存在的属性,会抛出AttributeError的错误,可以传入一个参数,,如果属性不存在,就返回默认值
print(getattr(xiaoc,"name1","属性不存在"))
实例属性和类属性
#实例属性
class Student(object):
def __init__(self, name):
self.name = name
s = Student('Bob')
s.score = 90
#类属性
class Student(object):
name = 'Student'
在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。