您好,登錄后才能下訂單哦!
單例模式,屬于創建類型的一種常用的軟件設計模式。通過單例模式的方法創建的類在當前進程中只有一個實例(根據需要,也有可能一個線程中屬于單例,如:僅線程上下文內使用同一個實例)
魔法方法是python內置方法,不需要主動調用,存在的目的是為了給python的解釋器進行調用,幾乎每個魔法方法都有一個對應的內置函數,或者運算符,當我們對這個對象使用這些函數或者運算符時就會調用類中的對應魔法方法,可以理解為重寫這些python的內置函數。
分頁顯示是一種非常常見的瀏覽和顯示大量數據的方法,屬于web編程中最常處理的事件之一。
類屬性應用需求: 對于京東商城中顯示電腦主機的列表頁面,每次請求不可能把數據庫
中的所有內容都顯示到頁面上,而是通過分頁的功能局部顯示,所以在向數據庫中請求
數據時就要顯示的指定獲取從第start條到第end條的所有數據 這個分頁的功能包括:
? 根據用戶請求的當前頁和總數據條數計算出 start 和 end
? 根據start 和 end 去數據庫中請求數據
? 是否有上一頁has_prev、下一頁has_next
? 上一頁prev、下一頁next
? 總頁數pages, 數據總條數total、當前頁信息items
"""
class Pagintor(object):
"""實現商品分頁的類"""
def __init__(self, objects_list, page=1, per_page=5):
"""
:param objects_list: 商品列表
:param page: 當前需要顯示的頁碼信息
:param per_page: 每頁顯示的數據個數
"""
self.objects_list = objects_list
self.page = page
self.per_page = per_page
@property
def start(self):
return (self.page - 1) * self.per_page
@property
def end(self):
return self.page * self.per_page
@property
def total(self):
"""
數據總條數total
:return:
"""
return len(self.objects_list)
@property
def pages(self):
"""
總頁數pages
if 總商品數量%每頁顯示數量==0: 剛好當前頁顯示滿
else: 好友多與的部分, 在計算的結果上面加1
self.total = 5 pages=1
self.total = 6 pages=2
:return:
"""
result = self.total // self.per_page
if self.total % self.per_page == 0:
return result
else:
return result + 1
@property
def has_next(self):
return True if 0 < self.page + 1 <= self.pages else False
@property
def next(self):
next_page = self.page - 1
next_start = (next_page - 1) * self.per_page
next_end = self.page * self.per_page
return self.objects_list[next_start:next_end]
@property
def has_prev(self):
return True if 0 < self.page - 1 <= self.pages else False
@property
def prev(self):
prev_page = self.page - 1
prev_start = (prev_page - 1) * self.per_page
prev_end = self.page * self.per_page
return self.objects_list[prev_start:prev_end]
@property
def items(self):
"""
當前頁信息items
:return:
"""
return self.objects_list[self.start:self.end]
if __name__ == '__main__':
# 應用場景二: 某一個屬性不能直接返回, 需要計算的, 可以通過property屬性實現
goods = ["電腦" + str(i) for i in range(5)]
#需求: 顯示第三頁時, 開始的索引是? 結束的索引為多少?
pagintor = Pagintor(goods, page=1, per_page=6)
print("第1頁的商品信息為: ", goods[pagintor.start:pagintor.end])
print("是否有上一頁?", pagintor.has_prev)
print("總頁數?", pagintor.pages)
"""
class Person(object):
def __init__(self, name, age):
self.name = name
self.__age = age #私有屬性
@property
def is_age_vaild(self):
return 0 < self.__age <= 150
def get_age(self):
if self.is_age_vaild:
return self.__age
else:
raise Exception("年齡不合法")
def set_age(self, age):
if self.is_age_vaild:
self.__age = age
else:
raise Exception("年齡不合法")
def del_age(self):
print("年齡屬性刪除......")
#類屬性 即:在類中定義值為property對象的類屬性
age = property(fget=get_age, fset=set_age, fdel=del_age)
if __name__ == '__main__':
p1 = Person("張三", 30)
print(p1.age)
p1.age = 31
print(p1.age)
del p1.age
class Person(object):
def __init__(self, name, age, score):
self.name = name
self.__age = age # 私有屬性
self.__score = score
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
self.__score = score
@property
def is_age_vaild(self):
return 0 < self.__age <= 150
@property # 獲取age屬性時執行的內容
def age(self):
if self.is_age_vaild:
return self.__age
else:
raise Exception("年齡不合法")
@age.setter # 設置age屬性時執行的內容
def age(self, age):
if self.is_age_vaild:
self.__age = age
else:
raise Exception("年齡不合法")
@age.deleter # 刪除age屬性時執行的內容
def age(self):
print("年齡屬性刪除......")
if __name__ == '__main__':
p1 = Person("張三", 30, 100)
print(p1.age) # 獲取年齡(), 執行@property def age(self):
p1.age = 31 # 設置年齡, age=31
print(p1.age)
del p1.age # 刪除年齡屬性
from functools import wraps
def singleton(cls):
"""
實現單例模式的裝飾器
思路: 當實例化對象時, 判斷該類是否實例化過對象。
- 如果是, 返回之前實例化的對象。
- 如果不是, 實例化第一個對象, 并將實例化后的對象存儲起來(緩存)。
"""
instances = {} # {'Person': obj}
@wraps(cls)
def wrapper(*args, **kwargs):
name = cls.__name__
if instances.get(name):
# 直接返回緩存中的對象
return instances.get(name)
else:
# 第一次實例化對象
obj = cls(*args, **kwargs)
#類名作為key值, 對象作為value值, 存儲到instances字典中.
instances[name] = obj
return obj
return wrapper
@singleton
class Person(object):
pass
if __name__ == '__main__':
p1 = Person()
p2 = Person()
#面試題目: ==和is有什么區別?
print("單例模式是否成功?", p1 is p2)
魔術方法
在Python中,所有用""包起來的方法,都稱為【魔術方法】(eg: len, init__)。
魔術方法一般是為了讓顯示器調用的,你自己并不需要調用它們。
特殊屬性:
dir
查看屬性
返回類或者對象的所有成員名稱列表。dir() 函數就是調用dir()。
1). 如果dir([obj]) 參數obj包含方法 dir(),該方法將被調用。
2). 如果Obj 不包含 dir(),該方法將最大限度收集屬性信息
python 中new , init , call的區別?
1). new的功能是在生成對象之前執行的內容,接受的參數是cls 類, 負責對象的創建
2). init的功能是在對象生成之后執行的內容, 接受的參數是self 對象, 負責對象的初始化
3). call的功能是在調用對象時執行的內容, 可以模擬函數的行為
from datetime import date
class Person(object):
def __new__(cls, *args, **kwargs):
print("判斷當前類是否擁有instance屬性?", hasattr(cls, 'instance'))
if not hasattr(cls, 'instance'):
cls.instance = super(Person, cls).__new__(cls)
return cls.instance
def __init__(self, name):
self.name = name
p1 = Person("張三")
p2 = Person("張xxx")
print("單例模式是否成功? ", p1 is p2)
class Person(object):
# 1). 設置類屬性, 存儲已經創建好的對象。
_instance = None
def __new__(cls, *args, **kwargs):
print("new方法在實例化對象之前執行.....返回對象本身")
#2). 判斷是否已經實例化對象?
if cls._instance:
return cls._instance
else:
self = object.__new__(cls)
cls._instance = self
#返回父類object的new方法創建的對象.....
return self
def __init__(self):
print("構造方法實例化對象之后執行......")
if __name__ == '__main__':
p1 = Person()
p2 = Person()
print(p1, p2)"""
1). new的功能是在生成對象之前執行的內容,接受的參數是cls 類, 負責對象的創建
2). init的功能是在對象生成之后執行的內容, 接受的參數是self 對象, 負責對象的初始化
3). call的功能是在調用對象時執行的內容, 可以模擬函數的行為.
"""
class Person(object):
def __new__(cls):
print("__new__")
return object.__new__(cls)
def __init__(self):
print("__init__")
def __call__(self, *args, **kwargs):
print('__call__')
def __del__(self):
# 析構方法: 當對象被刪除或者從內存釋放時自動執行
print("__del__")
p1 = Person()
p1()
from functools import lru_cache
class Fib(object):
@lru_cache(maxsize=1000)
def __call__(self, n):
if n in (1, 2):
return 1
else:
return self(n-1) + self(n-2)
fib = Fib()
print(fib(100)) # 1 1 2 3 5 8"""
可視化
類型判斷要使用type或isinstance, 不能通過判斷print輸出是否帶引號來判斷輸出值的類型。
1). str()與repr()都是python中的內置函數,是直接用來格式化字符串的函數。
2). 當使用內置函數str(obj)時, 自動執行obj.str()魔術方法。
3). 當使用內置函數repr(obj)時, 自動執行obj.repr()魔術方法。
4). 當str魔術方法不存在時, 自動執行repr()魔術方法的內容。
類型轉換
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __int__(self):
return int(self.age)
#def __str__(self):
#return 'Person<%s>' %(self.name)
def __repr__(self):
return 'Person<%s>' %(self.name)
p1 = Person("fentiao", '100')
print(p1)
print(int(p1))
索引與切片
拓展小知識: slice() 函數實現切片對象,主要用在切片操作函數里的參數傳遞。
索引&切片魔術方法:
setitem:當屬性被以索引、切片方式賦值的時候會調用該方法
getitem:一般如果想使用索引、切片訪問元素時,就可以在類中定義這個方法
delitem:當使用索引、切片刪除屬性時調用該方法
class Student(object):
def __init__(self, name, scores):
self.name = name
self.scores = scores
def __getitem__(self, index):
"""實現獲取索引和切片值的魔術方法"""
print(index)
return self.scores[index]
def __setitem__(self, index, value):
"""實現修改/設置索引和切片值的魔術方法"""
self.scores[index] = value
def __delitem__(self, index):
del self.scores[index]
def __mul__(self, other):
"""重復操作"""
return self.scores * other
def __add__(self, other):
"""連接操作, 傳入的時對象"""
return [ item[0]+item[1] for item in zip(self.scores, other.scores)]
def __contains__(self, item):
"""成員操作符"""
return item in self.scores
def __iter__(self):
# iter可以將可迭代對象轉換成迭代器(可以調用next方法的)
return iter(self.scores)
stu1 = Student("張三", [100, 90, 100])
stu2 = Student("里斯", [100, 80, 100])
#1). 索引和切片的測試
#print(stu1[1:]) # 獲取索引/切片值
#stu1[1:] = (80, 80) # 設置索引/切片對應的value值
#print(stu1.scores)
#del stu1[1:] # 刪除索引/切片值
#print(stu1.scores)
##2). 連接、重復和成員操作符
#print(stu1*3)
#print(stu1 + stu2)
#print(150 in stu1)
for item in stu1:
print(item)
with語句安全上下文
with語句操作的對象必須是上下文管理器。那么,到底什么是上下文管理器呢?
1). 簡單的理解,擁有 enter() 和 exit() 方法的對象就是上下文管理器。
enter(self):進入上下文管理器自動調用的方法,在 with 執行之前執行。如果 有 as子句,該
方法的返回值被賦值給 as 子句后的變量;該方法可以返回多個值。
exit(self, exc_type, exc_value, exc_traceback):退出上下文管理器自動調用的方法。在
with 執行之后執行(不管有無異常)。
2). 當 with as 操作上下文管理器時,就會在執行語句體之前,先執行上下文管理器的 enter() 方法,
然后再執行語句體,最后執行 exit() 方法。
構建上下文管理器,常見的有 2 種方式:基于類實現和基于生成器實現。
方法一: 裝飾器 contextlib.contextmanager,來定義自己所需的基于生成器的上下文管理器
方法二: 基于類的上下文管理器: 只要一個類實現了 enter() 和 exit() 這 2 個方
法,程序就可以使用 with as 語句來管理它
class Connect(object):
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.f = open(self.filename)
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
print('with語句執行之后......')
self.f.close()
#Connect就是上下文管理器。 擁有 __enter__() 和 __exit__() 方法的對象就是上下文管理器
with Connect('/etc/passwd') as conn:
pass
import contextlib
import tempfile
import shutil
@contextlib.contextmanager
def make_temp_dir():
try:
tmp_dir = tempfile.mkdtemp()
yield tmp_dir
finally:
shutil.rmtree(tmp_dir)
with make_temp_dir() as f:
pass
class Int(object):
def __init__(self, number, weight):
self.number = number
self.weight = weight
def __gt__(self, other):
"""判斷大于的魔術方法"""
return self.number * self.weight > other.number * other.weight
def __ge__(self, other):
"""判斷大于等于的魔術方法"""
return self.number * self.weight >= other.number * other.weight
def __eq__(self, other):
"""判斷等于的魔術方法"""
return self.number * self.weight == other.number * other.weight
i1 = Int(20, 3)
i2 = Int(20, 3)
print(i1 > i2)
print(i1 < i2)
print(i1 >= i2)
print(i1 == i2)
print(i1 != i2)
from functools import wraps
import time
def timeit(unit='s'):
def wrapper1(fun): # fun=add
@wraps(fun)
def wrapper(*args, **kwargs):
if unit == 's':
start_time = time.time()
result = fun(*args, **kwargs) # add(1, 2) result=3
end_time = time.time()
print("%s函數運行時間為%.2f s" %(fun.__name__, end_time-start_time))
return result
else:
print("當前功能不支持......")
return wrapper
return wrapper1
#類裝飾器: 裝飾器需要傳遞的參數通過__init__傳遞進入.被裝飾函數執行的內容在__call__魔術方法中編寫。
class TimeIt(object):
def __init__(self, unit='s'):
self.unit = unit
def __call__(self,fun):
@wraps(fun)
def wrapper(*args, **kwargs):
if self.unit == 's':
start_time = time.time()
result = fun(*args, **kwargs) #add(1, 2) result=3
end_time = time.time()
print("%s函數運行時間為%.2f s" % (fun.__name__, end_time - start_time))
return result
else:
print("當前功能不支持......")
return wrapper
#@timeit(unit='s') #@wrapper1 ==> add = wrapper1(add) ===> add =wrapper
#def add(num1, num2):
#time.sleep(0.333)
#return num1 + num2
"""
@TimeIt(unit='h')
#1). TimeIt_obj = TimeIt(unit='h')
#2). @TimeIt_obj
#3). add=TimeIt_obj(add)
#4). add = wrapper
"""
@TimeIt(unit='s')
def add(num1, num2):
time.sleep(0.333)
return num1 + num2
#add(1, 2) ==> wrapper(1, 2)
add(1, 2)
from functools import partial
max_100 = partial(max,10, 100) # 返回對象
print(max_100(1, 2, 3)) # 100"""
from wtforms import StringField,SubmitField
import os
#作為基類/父類
class FileAcceptor(object):
def __init__(self, accepted_extensions):
"""
eg: ['.png', '.jpg']
:param accepted_extensions: 可以接受的擴展名
"""
self.accepted_extensions = accepted_extensions
def __call__(self, filename):
"""
eg: hello.jpg
:param filename: 需要判斷的文件名
:return:
"""
#base = 'hello', ext='.jpg'
base, ext = os.path.splitext(filename)
return ext in self.accepted_extensions
#子類
class ImageFileAcceptor(FileAcceptor):
def __init__(self):
image_ext = ('.jpg', '.jepg', '.png')
super(ImageFileAcceptor, self).__init__(image_ext)
#子類
class ExcelFileAcceptor(FileAcceptor):
def __init__(self):
image_ext = ('.xls', '.xlsx')
super(ExcelFileAcceptor, self).__init__(image_ext)
if __name__ == '__main__':
filenames = [
'hello.jpg',
'hello.xls',
'hello.txt'
]
"""
1). ImageFileAcceptor() 實例化對象, 執行__new__和__init__魔術方法。
2). imagefileacceptor_obj
3). imagefileacceptor_obj('hello.jpg') True
3). imagefileacceptor_obj('hello.xls') False
3). imagefileacceptor_obj('hello.txt') False
4). ['hello.jpg']
"""
images_file = filter(ImageFileAcceptor(), filenames)
excels_file = filter(ExcelFileAcceptor(), filenames)
print(list(images_file))
print(list(excels_file))
def create_class(name):
if name == 'foo':
class Foo(object):
pass
return Foo
else:
class Bar(object):
pass
return Bar
cls = create_class(name='foo1')
print(cls.__name__)
#type函數語法:
#type(類名, 父類名稱的元組, 屬性信息)
#class Person(object):
#country= 'China'
def hello(self):
print("hello")
Person = type('Person',(object, ), {'country':'China', 'hello':hello})
p1 = Person()
print(p1.country)
p1.hello()
魔術方法匯總
基本的魔法方法
有關屬性的魔術方法
比較操作符
算數運算符
反運算
增量賦值運算
一元操作符
類型轉換
上下文管理(with 語句)
容器類型
元類
類也是對象
Python一切皆對象
Linux一切皆文件
元類是類的類,是類的模板
元類的實例為類,正如類的實例為對象。
類的本質是對象, 于是可以對類做如下的操作:
#實現單例模式的方法:
#1. 裝飾器
#2. new魔術方法
#3. metaclass自定義元類
class Singleton(type):
type(name, bases, attrs)
自定義元類實現單例模式, 父類是type
#所有類和實例化對象之間的關系; eg: {'Person': Pseron()}
cache = {}
#1). 為什么是__call__魔術方法?
def __call__(cls):
#判斷類是否已經實例化, 如果沒有, 實例化后存儲到緩存中。 最后將緩存的信息返回給用戶。
if cls not in cls.cache:
cls.cache[cls] = super(Singleton, cls).__call__()
return cls.cache[cls]
#type('Pseron', (), {})
#創建以各類Person, 指定創建Person類的類(元類)是type.
#2. metaclass是在做什么? 指定元類為Singleton。
class Person(object, metaclass=Singleton): # Person = Singleton.__new__(Person, (objects, ), {})
pass
#Person是Singleton元類實例化出的對象, Person()就是對象(), 執行Singleton.__call__魔術方法.
p1 = Person()
p2 = Person()
print(p1, p2)
#99%情況不需要自己自定義元類。
抽象基類
抽象基類有兩個特點:
1.規定繼承類必須具有抽象基類指定的方法
2.抽象基類無法實例化
基于上述兩個特點,抽象基類主要用于接口設計
實現抽象基類可以使用內置的abc模塊
import abc
class Human(metaclass=abc.ABCMeta):
"""基類, 定義一個抽象類"""
@abc.abstractmethod
def introduce(self):
print("introduce.....")
@abc.abstractmethod
def hello(self):
print('hello')
class Person(Human):
# 1).規定繼承類必須具有抽象基類指定的方法
def introduce(self):
print('person')
def hello(self):
print('person hello')
#2). 抽象基類無法實例化
#h = Human()
p = Person()
p.introduce()
p.hello()
動態語言與靜態語言的不同?
import time
from datetime import date
#d = date.today()
#print("對象類型: ", type(d)) # <class 'datetime.date'>
#print("判斷是否有year這個屬性?", hasattr(d, 'year')) # True
#print("判斷是否有time這個屬性?", hasattr(d, 'time')) # False
##setattr(d, 'time', '10:10:10') # 報錯
class Date(object):
# __slots__ 來限制該對象能添加的屬性信息
__slots__ = '__year', '__month', '__day'
def __new__(cls, year, month, day):
self = object.__new__(cls)
self.__year = year
self.__month = month
self.__day = day
return self
@property
def year(self):
return self.__year
@property
def month(self):
return self.__month
@property
def day(self):
return self.__day
@classmethod
def today(cls):
time_t = time.localtime()
return cls(time_t.tm_year, time_t.tm_mon, time_t.tm_mday)
def __str__(self):
return '%s-%s-%s' %(self.__year, self.__month, self.__day)
d = Date(2019, 10, 10)
print("對象類型: ", type(d)) # <class 'datetime.date'>
print("判斷是否有year這個屬性?", hasattr(d, 'year')) # True
print("判斷是否有time這個屬性?", hasattr(d, 'time')) # False
#setattr(d, 'time', '10:10:10') # Error
#print('time:', getattr(d, 'time')) # Error
print(Date.today())
#1). 整數在程序中的使用非常廣泛,Python為了優化速度,使用了小整數對象池,
#避免為整數頻繁申請和銷毀內存空間。
#2). Python對小整數的定義是[-5,257)
>>> a = 1
>>> id(a)
139883638752032
>>> b = 1
>>> id(b)
139883638752032
>>> c = 257
>>> d = 257
>>> id(c), id(d)
(139883633580400, 139883633580432)
>>> e=-5;f=-5
>>> id(e), id(f)
(139883638751840, 139883638751840)
#********************************2. 字符串駐留機制 ********************************
#1). string interning(字符串駐留): 它通過維護一個字符串常量池(string intern pool),
#從而試圖只保存唯一的字符串對象,達到既高效又節省內存地處理字符串的目的。
>>> a = 'hello'
>>> b = 'hello'
>>> id(a), id(b)
(139883511138480, 139883511138480)
>>> c = 'pythonchjdshfcejhfkjrehfkjrehfkjrehfregjrkhgkjrg'
>>> d = 'pythonchjdshfcejhfkjrehfkjrehfkjrehfregjrkhgkjrg'
>>> id(c),id(d)
(139883633099360, 139883633099360)
#2). 字符串(含有空格),不可修改,沒開啟intern機制,不共用對象,引用計數為0,銷毀。
>>> a = 'a b'
>>> b = 'a b'
>>> id(a), id(b)
(139883511138608, 139883511138544)
#導致引用計數+1的情況
>>> #1). 對象被創建,例如a=23
...
>>> name = 'fentiao'
>>>
>>> #2). 對象被作為參數,傳入到一個函數中
...
>>> import sys
>>> sys.getrefcount(name)
2
>>> #3). 對象被引用,例如b=a
...
>>> cat_name = name
>>> sys.getrefcount(name)
3
>>> #4). 對象作為一個元素,存儲在容器中,例如list1=[a,a]
...
>>> l = [name, 'hello', 'python']
>>> sys.getrefcount(name)
4
#***************************導致引用計數-1的情況*************************
>>> #1). 對象的別名被顯式銷毀,例如del a
...
>>> del cat_name
>>> sys.getrefcount(name)
3
>>> #2). 對象的別名被賦予新的對象,例如a=24
>>> name1 = name
>>> sys.getrefcount(name)
4
>>> name1 = 'hello'
>>> sys.getrefcount(name)
3
>>> #4). 對象所在的容器被銷毀,或從容器中刪除對象
...
>>> l
['fentiao', 'hello', 'python']
>>> del l[0]
>>> sys.getrefcount(name)
2
#****************************gc模塊使用***********************************
#1). 分代回收的頻率
>>> gc.get_threshold()
(700, 10, 10)
>>>gc.set_threshold(700, 90, 90)
KeyboardInterrupt
#2). 垃圾回收機制是否開啟
>>>gc.isenabled()
True
>>>gc.disable()
>>>gc.isenabled()
False
>>> gc.enable()
>>>gc.isenabled()
True
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。