补充:包导入的一个小问题,在包的__init__文件中,from…import…,在运行程序中导入包,调用包内模块时,依然要添加前缀包.模块.变量,主要原因是命名空间,在运行文件中,包名指向了包的init文件,而init文件中的导入模块,是模块名指向目标模块,因此才通过包名.模块调用,第一个包名是告诉程序进入包的init文件,第二个模块名是告诉程序在包的init文件进入模块命名空间。另外在包的init文件使用from…import…时,因为时运行当前文件,因此from要符合当前文件的sys.path。
1、面向对象-封装
先看下面的代码
import datetime
class Book:
count = 0 # 既可以通过类调用,也可以通过实例调用,但实例不能修改,如果自己修改,自己就会创建一个内置属性跟这个重名
# 初始化函数
def __init__(self, title, price=0.0, author=' ', pubdata=datetime.date.today(), publisher=None): # 预定义的,构造函数,有什么属性
self.title = title # self传进去的不懂实例
self.price = price #价格
self.author = author #作者
self.pubdata = pubdata #出版时间
self.__publisher = publisher #私有变量出版社
Book.count += 1
def print_info(self):
print('当前这本书的信息如下')
print('标题{}'.format(self.title))
print('定价{}'.format(self.price))
print('作者{}'.format(self.author))
print('出版社{}'.format(self.publisher))
print('出版日期{}'.format(self.pubdata))
# 在控制台输入对象名时的返回值
def __repr__(self):
return '图书{}'.format(self.title)
# print时候用的,如果没有定义这个,print打印时会调用__repr__
def __str__(self):
return '[图书:{}]'.format(self.title)
# 析构函数
def __del__(self):
Book.count -= 1
# 类函数,与实例没关系,python3里可以不写参数,但pycharm会提示,但可以正常运行
def cls_method(cls):
print('类函数')
def static_method():
print('静态函数,与参数无关')
#既可以通过类调用,也可以通过对象调用
@staticmethod
def static_method1():
print('静态函数,与参数无关')
在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。
book1 = Book('C#', 29.00, 'Tom', datetime.date(2016, 3, 1), 'youpinketang')
print(book1.title)
book1.print_info()
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。上面程序的__publisher就是私有变量,也可以通过_Book__publisher来访问私有变量,但不建议这么使用。
count属于类变量,既可以通过类调用,也可以通过实例调用,但实例不能修改,如果自己修改,自己就会创建一个内置属性跟这个重名。
print('图书的数量时{}'.format(Book.count))
print('图书的数量时{}'.format(book1.count))
__repr__(self)函数是用在控制台中直接输入类的对象的输出,当程序没有定义__str__(self)时,调用打印时也会调用__repr__。当程序定了__str__时,调用打印时,程序会使用__str__的返回值。
book2 = Book('Flask')
print(book2)
注:datetime.data(年,月,日)会生成一个年月日的对象,调用打印时可以生成年-月-日,datetime.data.today()可以获得当前的年月日,datetime.data.today().year可以返回当年的年份
上述程序中__init__是初始化方法,当定义一个对象时,会自动调用;__del__时析构函数,当一个对象被销毁之前会调用这个函数。
当定义一个方法的参数时cls(python2的用法)或者不给其提供参数时,这个方法是类的方法,可以通过类直接调用,如果定义是有cls,则使用时也要传入一个类的对象。通过对象无法调用。如果一个方法前加了 @staticmethod的装饰器,则这个方法类或者对象都可以调用
Book.cls_method(book2) # 参数不能省,必须得传一个实例
Book.static_method()
book2.static_method1()
Book.static_method1()
如下代码:
import datetime
class Student:
def __init__(self, name, birth):
self.name = name
self.birth = birth
def get_age(self):
return datetime.date.today().year - self.birth.year
# 属性装饰器
@property
def age(self):
return datetime.date.today().year - self.birth.year
@age.setter
def age(self, value):
raise AttributeError('禁止赋值年龄!')
@age.deleter
def age(self):
raise AttributeError('不能删除')
程序中的@property叫做装饰器,它使得一个方法可以像调用属性一样被调用。
print(s.age)
@age.setter、@age.deleter与装饰向对应使用,分别时给age赋值时调用,删除age时调用
s.age = 20
del (s.age)
2、面向对象-继承
在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。
import datetime
class Department:
def __init__(self,department,phone,manager):
self.department = department
self.phone = phone
self.manager = manager
def __repr__(self):
return '<部门>:{}'.format(self.department)
class Employee:
def __init__(self, department:Department, name, birth, salary):
self.department = department
self.name = name
self.birth = birth
self.salary = salary
def __repr__(self):
return '员工:{}'.format(self.name)
@property
def age(self):
return datetime.date.today().year - self.birth.year
def give_raise(self, percent, bonus=0.0):
self.salary = self.salary * (1 + percent + bonus)
def working(self):
print('员工:{}正在工作'.format(self.name))
class Programer(Employee):
def __init__(self, department, name, birth, salary, speciality, project):
super().__init__(department, name, birth, salary)
self.speciality = speciality
self.project = project
def working(self):
print('程序员:{}正在开发项目:{}'.format(self.name, self.project))
class HR(Employee):
def __init__(self,department, name, birth, salary, level=1):
Employee.__init__(self,department, name, birth, salary)
self.level =level
在上述代码中,继承时,在定义的类开始加一个括号,写明继承的父类,python支持多继承,当发生多继承时,super显得不明确,因此可以使用类名.方法的方式进行集成初始化定义。
在类中,某个参数时一个确定类型,可以通过:类型来提示用户,但并不是强制的,用户依然可以改用其他参数。