1.类与对象的定义
1.1 全都是对象
- python完全支持面向对象的基本功能:如封装、继承、多态以及对基类的重写和覆盖
- python中一切内容都是对象,包括字符串、列表、元组、字典、集合、range对象等,函数也是对象、类也是对象
1.2 创建第一个完整的类
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Employee:
'所有员工的基类'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
1.3 构造方法
Python中的构造函数时__init__(),一般来说时给类的属性设置初值或者对其它必要的初始化工作,在创建对象的时候会被自动执行。在__init__()方法中定义的属性是实例属性,可以记录这个对象的特别信息。在类的方法中可以通过对self属性的方式获得__init__()方法中初始的值。
class people(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def func(self):
print(f"{self.name}的性别是{self.gender}")
面向对象编程的一个特点就是数据封装,在people类的__init__方法中初始化的实例属性name和gender,可以在类的方法通过类的对象访问类的实例属性。
if __name__ == '__main__':
xiaoming = people("小明", "男")
xiaoming.func()
输出结果:小明的性别是男
也可以通过类的对象访问实例属性,类的实例属性可以被类的对象访问和修改
if __name__ == '__main__':
xiaoming = people("小明", "男")
xiaoming.name = "张三"
xiaoming.func()
结果:张三的性别是男
2.私有属性和方法
在类的内部中可以定义属性和方法,在方法内封装复杂的业务逻辑,可以通过类的对象访问方法。但是类的属性可以在类的内部进行访问和修改,也可以通过类的对象进行访问和修改,这样会破坏类的封装性。
在java中我们可以设置javabean,用private修改属性的访问权限,给属性添加get和set方法。那么在python中我们要私用属性和方法改怎么做呢?我们可以在属性的名称前面加上两个下划线"__"。
2.1 定义私有属性
python中定义类的属性时,如果属性的名称以两个下划线开头则是表示为私有属性。私有属性在类的外部不能访问,需要通过类调用对象的公有方法进行访问。子类可以继承父类的公有成员,但是不能继承私有成员。
class people(object):
def __init__(self, name, gender):
self.__name = name
self.__gender = gender
def func(self):
print(f"{self.__name}的性别是{self.__gender}")
if __name__ == '__main__':
xiaoming = people("小明", "男")
xiaoming.name = "张三"
xiaoming.func()
输出结果:小明的性别是男
我们发现不能再通过对象.属性方式修改类的成员变量,并且当我们打印print(xiaoming.__name) 发现程序报错:
AttributeError: ‘people’ object has no attribute ‘__name’
2.2 定义私有方法
除了定义私有属性,还可以定义私有方法,在方法名前面加上两个下划线即可。那么我们如何访问私有方法呢,很简单,通过一个公有方法去调用私有方法即可。
class people(object):
def __init__(self, name, gender):
self.__name = name
self.__gender = gender
def __func(self):
print(f"{self.__name}的性别是{self.__gender}")
def info(self):
self.__func()
if __name__ == '__main__':
xiaoming = people("小明", "男")
xiaoming.info()
输出结果:小明的性别是男
3.继承
在OOP(面向对象程序设计)中继承是为了代码复用和设计复用而设计的。设计一个新类时,如果可以继承一个已有的设计好的类进行二次开发,可以大幅度减少代码量。在继承关系中,已有的,设计好的类被称为父类或者基类,新设计的类被成为子类或者派生类,享有父类的属性和方法。在python中同时支持单继承和多继承(在java中只支持单继承,多继承要通过接口来实现)
3.1 单继承
python中单继承语法:
class newclass(parentclass):
pass
当子类继承父类时,在子类的构造函数中调用父类的构造函数有两种写法:
- 经典类的写法:父类._init_(self,kwargs1,kwargs2,…)
- 新式类的写法: super(子类,self)._init_(self,kwargs1,kwargs2,…)
本例编写两个类,一个people类,一个student,student继承people
class people(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def func(self):
print(f"{self.name}的性别是{self.gender}")
class student(people):
def __init__(self,name,gender,age):
super(student, self).__init__(name, gender)
self.age = age
def stu_fun(self):
print(f"{self.name}的性别是{self.gender},年龄是{self.age}")
if __name__ == '__main__':
stu = student("小明","男","18")
stu.stu_fun()
stu.func()
结果:
小明的性别是男,年龄是18
小明的性别是男
3.2 子类对父类方法的重写
有时候,子类不想继承父类的方法,而是对父类的方法进行一定的修改,这个时候就要对父类方法进行重写。如果子类和父类方法有相同的方法名,输入参数和返回类型,那么新方法会覆盖旧方法。
如果在子类中要调用父类的方法,可以用super()方法名的方式进行调用。
class people(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def func(self):
print(f"{self.name}的性别是{self.gender}")
class student(people):
def __init__(self, name, gender, age):
super(student, self).__init__(name, gender)
self.age = age
def func(self):
super().func()
print(f"{self.name}的性别是{self.gender},年龄是{self.age}")
if __name__ == '__main__':
stu = student("小明", "男", "18")
stu.func()
结果:
小明的性别是男
小明的性别是男,年龄是18
本例中,student类继承了people类,在student类中重写了父类的方法func(),还在子类重写的方法中调用了父类的方法。
3.3 多继承(继承下的多态)
什么叫做多态呢?
当子类和父类都有相同的方法时,子类的方法覆盖父类的方法,在代码运行的时候,总是会调用子类的方法,这就是多态。
python中多继承语法:
class newclass(parentclass1,parentclass2,...)
pass
需要注意的是newclass后面的父类顺序,如果父类中有相同的方法名,而在子类中没有使用指定的父类名,则python解释器会从左向右按顺序进行搜索,如果方法在子类中没找到,则会从左向右查找父类中是否包含该方法。
class Man(object):
def __init__(self, name):
self.name = name
self.gender = "男"
def func(self):
print(f"{self.name}的性别是{self.gender}")
class Woman(object):
def __init__(self, name):
self.name = name
self.gender = "女"
def func(self):
print(f"{self.name}的性别是{self.gender}")
class Student(Man, Woman):
def __init__(self, name, age):
super(Student, self).__init__(name)
self.age = age
# def func(self):
# super().func()
# print(f"{self.name}的性别是{self.gender},年龄是{self.age}")
if __name__ == '__main__':
stu = Student("小明", "18")
stu.func()
结果:小明的性别是男
4.静态方法
静态方法是类中的函数,不需要实例。静态方法主要放一些逻辑性代码,主要是一些逻辑属类,但是和类本身没有什么交互,即在静态方法中,不会涉及类中方法和属性的操作。静态方法定义的时候要用@staticmethod装饰器
静态方法可以通过类名访问,也可以通过实例访问
class OperateNum(object):
def __init__(self,num1,num2):
self.num1 = num1
self.num2 = num2
@staticmethod
def sum(a,b):
return a+b
def fun_sum(self):
res = OperateNum.sum(self.num1,self.num2)
print(res)
if __name__ == '__main__':
test = OperateNum(1,2)
test.fun_sum()
print(test.sum(1,2))
结果:
3
3
静态方法不需要创建对象就可以访问类中的方法。
5.魔法方法和特殊属性
魔法方法也被称为特殊方法,总是被双下划线包围,如:_init_(),特殊属性也是以双下划线包围的属性,如:_dict_(),_slots_()。
5.1 dict
__dict__可以访问类的所有属性,以字典的方式返回
class Student(object):
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
def func(self):
print(f"{self.name}的性别是{self.gender},年龄是{self.age}")
if __name__ == '__main__':
xiaoming = Student("小明", "男", 18)
print(xiaoming.__dict__)
print(xiaoming.__dict__['name'])
xiaoming.func()
结果:
{'name': '小明', 'gender': '男', 'age': 18}
小明
小明的性别是男,年龄是18
5.2 slots
Python中新类增加了__slots__内置属性,可以把实例属性锁定到__slots__规定的范围内。在python中,我们创建一个类的实例后,可以给这个实例绑定任何属性和方法,这就是动态语言的灵活性。
class Student(object):
pass
给动态实例绑定一个属性
s = Student()
s.name = 'zhangsan'
print(s.name)
如果要限制实例的属性怎么办呢?例如只对Student实例添加name和age属性。限制实例的属性就要用到__slots__特殊属性,这个属性可以用元组定义允许绑定的属性名称。只有这个元组中出现的属性可以被类实例调用。
class Student(object):
__slots__ = ("name", "age", "gender")
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
if __name__ == '__main__':
s = Student("张三", "18", "男")
print(f"{s.name},{s.age},{s.gender}")
s.score = 90
print(s.score)
结果:
AttributeError: 'Student' object has no attribute 'score'
张三,男,18
报错的原因是:score没有在__slots__中,不能绑定score属性。值得注意的是:__slots__定义的属性仅能对当前的类实例起作用,对继承它的子类是不起作用的。
class People(object):
__slots__ = ("name", "age", "gender")
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
class Student(People):
def __init__(self,name, gender, age):
super(Student, self).__init__(name, gender, age)
if __name__ == '__main__':
s = Student("张三", "18", "男")
print(f"{s.name},{s.age},{s.gender}")
s.score = 90
print(s.score)
运行结果:
张三,男,18
90
输出结果显示,子类的实例不受父类__slots__束缚
5.3 str()
不知道大家再写程序是,打印一个实例化对象时,打印的其实时一个对象的地址。而通过__str__()函数就可以帮助我们打印对象中具体的属性值,或者你想得到的东西。
因为再python中调用print()打印实例化对象时会调用__str__()如果__str__()中有返回值,就会打印其中的返回值。
class ss:
def __init__(self,age,name):
self.age = age
self.name = name
def __str__(self):
return str(self.age)+",,wozenmezhemeshuai,,"+self.name
if __name__=="__main__":
s = ss(21,'aitebao')
print(s)
结果:
21,,wozenmezhemeshuai,,aitebao
5.4 iter()
如果想要一个类用于for…in循环,类似元组或者列表,就必须实现__iter__()方法。该方法返回一个迭代对象,python中的for循环会不断调用该迭代对象的__next__()方法,获得循环的下一个值,下面以斐波那契数列为例,写一个可以作用与for循环的Fib类。
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.a > 100:
raise StopIteration
return self.a
for n in Fib():
print(n)
结果:
1
1
2
3
5
8
13
21
34
55
89
5.5 getitem()
Fib实例虽然可以作用于for循环,但是,不能将它作为列表来用,比如获取第四个元素。
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.a > 100:
raise StopIteration
return self.a
for n in Fib()[3]:
print(n)
发现报错:
for n in Fib()[3]:
TypeError: 'Fib' object is not subscriptable
如果要获取第四个元素,需要实现__getitem__()方法,代码如下:
class Fib(object):
def __getitem__(self, item):
a, b = 1, 1
for x in range(item):
a, b = b, a + b
return a
fib = Fib()
print(fib[3]) # 结果是: 3
5.6 getattr()
如果调用类的方法或者属性时,类的方法或者属性不存在,就会报错。要避免这个错误,可以使用__getattr_()方法动态的返回一个属性。
class Student(object):
def __init__(self, name):
self.name = name
def __getattr__(self, item):
if item == 'age':
return 18
stu = Student(name="xiaoming")
print(stu.age)
# 18
当调用不存在的属性age时,python解释器会调用__getattr__(self,‘age’)以尝试获取属性,这样就会返回age属性的值。
5.7 getattribute
因为python中所有类默认继承object类。而object类提供了了很多原始的内建属性和方法,所以用户自定义的类在Python中也会继承这些内建属性。可以使用dir()函数可以查看,虽然python提供了很多内建属性但实际开发中常用的不多。而很多系统提供的内建属性实际开发中用户都需要重写后才会使用。对于python来说,属性或者函数都可以被理解成一个属性.
— 版权声明:本文为CSDN博主「涤生大数据」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_26442553/article/details/82467777
1.重写__getattribute__方法
class Student(object):
country = "china" #类属性不会放到__dict__中
def __init__(self,name,age):
self.name = name
self.age = age
def __getattribute__(self, attr): #注意:attr是传入的属性名,不是属性值
print("开始属性校验拦截功能")
print(attr)
return object.__getattribute__(self, attr) #返回属性名
s1 = Student("tom",19)
print(Student.country,s1.country,s1.name,s1.age) #调用属性,会调用__getattribute__方法
'''注意结果,调用了四次属性,但是却只调用了3次 __getattribute__方法。
开始属性校验拦截功能
country
开始属性校验拦截功能
name
开始属性校验拦截功能
age
china china tom 19
'''
分析总结:
- __getattribute__是属性访问拦截器,就是当这个类的属性被实例访问时,会自动调用类的__getattribute__方法。当实例调用属性时,比如s1.name,会把name作为实参传进__getattribute__方法中,经过一系列操作后,再把name处理后的结果返回。Python中只要定义了继承object的类,就默认存在属性拦截器,只不过是拦截后没有进行任何操作,而是直接返回。所以我们可以自己改写__getattribute__方法来实现相关功能,比如查看权限、打印log日志等。
- 如果重写了__getattribute__,则类会调用重写的方法,所以这个方法必须要有renturn返回值,返回传进去的属性,否则调用属性会出现失败的情况。
- 注意如果是直接用类名.类属性的形式调用类属性,是不会调用 __getattribute__方法。
2.重写__getattribute__实现属性拦截功能
class Student(object):
country = "china" #类属性不会放到__dict__中
def __init__(self,name,age):
self.name = name
self.age = age
def __getattribute__(self, attr): #注意:attr是传入的属性名,不是属性值
print("开始属性校验拦截功能")
print(attr)
if attr == "name": #注意这里引用原属性名不用self,直接引号引起来即可。
print("现在开始调用的是name属性")
elif attr =="age":
print("现在开始调用的是age属性")
else:
print("现在调用的是其他属性")
return object.__getattribute__(self, attr) #返回属性名
s1 = Student("tom",19)
print(s1.name,s1.age,s1.country)
'''结果如下:
开始属性校验拦截功能
name
现在开始调用的是name属性
开始属性校验拦截功能
age
现在开始调用的是age属性
开始属性校验拦截功能
country
现在调用的是其他属性
tom 19 china
'''
5.8 getattr
如果在类中定义了这个方法,并且在该类字典中找不到相应属性的时候,会调用这个方法。
实例instance通过instance.name访问属性name,只有当属性name没有在实例的__dict__或它构造类的__dict__或基类的__dict__中没有找到,才会调用__getattr__。
当属性name可以通过正常机制追溯到时,getattr__是不会被调用的。如果在__getattr(self, attr)存在通过self.attr访问属性,会出现无限递归错误。
class ClassA(object):
def __init__(self, classname):
self.classname = classname
def __getattr__(self, attr):
return('invoke __getattr__', attr)
insA = ClassA('ClassAname')
print(insA.__dict__) # 实例insA已经有classname属性了
# {'classname': 'ClassA'}
print(insA.classname) # 不会调用__getattr__
# ClassA
print(insA.grade) # grade属性没有找到,调用__getattr__
# ('invoke __getattr__', 'grade')
print(insA.__dict__) # 实例insA已经有classname属性了
# {'classname': 'ClassA'}
#***********************************************************************
print(getattr(insA,'classname'))
#ClassAname
print(getattr(insA,'grade'))
# ('invoke __getattr__', 'grade')
setattr(insA, "grade", "18") #增加属性
print(insA.__dict__)
#{'classname': 'ClassAname', 'grade': '18'}
print(insA.grade)
#18
5.9 setattr
setattr:无论是直接赋值还是通过内置的setattr函数赋值,都会调用。。。基类一般会内置__setattr__()方法
如果类自定义了__setattr__方法,当通过实例获取属性尝试赋值时,就会调用__setattr__。
常规的对实例属性赋值,被赋值的属性和值会存入实例属性字典__dict__中。
class ClassA(object):
def __init__(self, classname):
self.classname = classname
insA = ClassA('ClassA')
print(insA.__dict__)
# {'classname': 'ClassA'}
insA.tag = 'insA' #增加属性,正常
print(insA.__dict__)
# {'tag': 'insA', 'classname': 'ClassA'}
#********************************************************
setattr(insA, "age", "18") #增加属性,正常
print(insA.__dict__)
#{'tag': 'insA', 'classname': 'ClassA', 'age': '18'}
#********************************************************
#显式调用
insA.__setattr__('tag1','insA1')
print(insA.__dict__)
#{'tag1': 'insA1', 'tag': 'insA', 'classname': 'ClassA', 'age': '18'}
#********************************************************
#从基类继承了__setattr__()方法
print(dir(insA))
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__',
#'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__',
#'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
#'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age',
#'classname', 'tag', 'tag1']
如下类自定义了__setattr__,对实例属性的赋值就会调用它。类定义中的self.attr也同样,所以在__setattr__下还有self.attr的赋值操作就会出现无线递归的调用__setattr__的情况。自己实现__setattr__有很大风险,一般情况都还是继承object类的__setattr__方法。 __ setattr __:无论是直接赋值还是通过内置的setattr函数赋值,都会调用。
class ClassA(object):
def __init__(self, classname):
self.classname = classname
def __setattr__(self, name, value):
# self.name = value # 如果还这样调用会出现无限递归的情况
print('invoke __setattr__')
insA = ClassA('ClassAname') # __init__中的self.classname调用__setattr__。
# invoke __setattr__
print(insA.__dict__)
# {} #注意这里
insA.tag = 'insA'
# invoke __setattr__
print(insA.__dict__)
# {}
#********************************************************
setattr(insA, "age", "18")
#invoke __setattr__
print(insA.__dict__)
# {}
5.10 delattr
与__setattr __()类似,但用于删除属性而不是赋值。只有当del obj.name对对象有意义时才应该实现。
6.可调用对象
在python中函数也是一个对象。实际上,任何一个有__call__()特殊方法的对象都是函数,示例如下:
class Add(object):
def __call__(self, *args):
return sum(*args)
add = Add() # 定义函数对象,可以把函数对象看作函数
print(add([1,2,3,4])) # 调用函数,结果为:10
所有的函数都是可调用函数。由于add可以调用,所以add被称为可调用对象。
一个实例也可以变成一个可调用对象,只需要实现__call__()方法,比如把People类变成一个可调用对象。
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __call__(self, friend):
print(self.name, self.age)
print(friend)
if __name__ == '__main__':
p = Person("小明", 18)
p("张三")
结果:
小明 18
张三
仅仅看p(“张三”)无法确定Person是一个函数还是一个类实例,所以在python中,函数也是对象,对象和函数区别不显著,这也是python灵活的地方。
那么如何判断一个变量是函数还是对象呢?
很多时候判断一个对象能否被调用,可以使用callable()函数。
print(callable(p)) # True
print(callable(["a"])) # False
print(callable(max)) # True
由输出结果可以知道,通过callable()函数可以判断一个对象是否是可调用对象。
7.补充Python内置类属性
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Employee:
'所有员工的基类'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
print "Employee.__doc__:", Employee.__doc__
print "Employee.__name__:", Employee.__name__
print "Employee.__module__:", Employee.__module__
print "Employee.__bases__:", Employee.__bases__
print "Employee.__dict__:", Employee.__dict__
执行结果如下:
Employee.__doc__: 所有员工的基类
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount': <function displayCount at 0x10a939c80>, 'empCount': 0, 'displayEmployee': <function displayEmployee at 0x10a93caa0>, '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe5\x91\x98\xe5\xb7\xa5\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', '__init__': <function __init__ at 0x10a939578>}