GVKun编程网logo

pprint字典多行

18

在本文中,我们将详细介绍pprint字典多行的各个方面,同时,我们也将为您带来关于.NET进阶:readOnly、Concurrent字典,如何写出线程安全字典、C#字典多线程读写、C语言随笔_pri

在本文中,我们将详细介绍pprint字典多行的各个方面,同时,我们也将为您带来关于.NET进阶:readOnly、 Concurrent字典,如何写出线程安全字典、C#字典多线程读写、C语言随笔_printf输出多行 - 知乎、Go 语言中 Print,Println 和 Printf 的区别(八)的有用知识。

本文目录一览:

pprint字典多行

pprint字典多行

我正在尝试漂亮地打印字典,但是我没有运气:

>>> import pprint
>>> a = {'first': 123,'second': 456,'third': {1:1,2:2}}
>>> pprint.pprint(a)
{'first': 123,'third': {1: 1,2: 2}}

我希望输出在多行中,如下所示:

{'first': 123,2: 2}
}

pprint做到吗?如果不是,那么它是哪个模块?我正在使用Python
2.7.3

.NET进阶:readOnly、 Concurrent字典,如何写出线程安全字典

.NET进阶:readOnly、 Concurrent字典,如何写出线程安全字典

.NET进阶:readOnly、 Concurrent字典,如何写出线程安全字典

Part 0 你所不知的特殊字典

在日常的开发中,论使用率最高的容器,List和Dictionary数一数二。

这里主要想讲讲作为初学者的你,可能不知道的字典。

  • ConcurrentDictionary<TKey, TValue>:这是一个线程安全的字典实现,允许多个线程同时读写而不需要额外的同步机制。它在多线程环境中非常有用,可以提高性能并减少锁的开销。例如,如果你需要在多个线程之间共享字典数据,ConcurrentDictionary 是一个很好的选择。
  • ReadOnlyDictionary<TKey, TValue>:这个类提供了一个只读的字典视图,一旦创建,就不能修改字典的内容。这在你需要防止字典被修改,但又想提供字典内容的访问时非常有用。例如,你可以将一个可变的字典转换为只读字典,然后将其传递给不需要修改字典的组件或方法。
  • SortedDictionary<TKey, TValue>:这个字典类型按照键的顺序存储元素,并且可以在O(log n)时间内进行查找、插入和删除操作。它适合于需要按键的顺序访问元素的场景。

这里我们展开聊聊前两种。

Part 1 Concurrent字典

刚才已经提过了,这是一个线程安全字典。你使用多线程或者异步操作时,字典内部有一套同步机制,会防止死锁。

这里聊一些比较常用的扩展方法。不同的.NET版本、.NET core可能会导致细微差别,但是使用的方式都是差不多的。

1. GetOrAdd

顾名思义,同时包含了Get和Add的一个方法,返回值是对应的TValue,也就是你定义字典的值的类型。这个方法会先试图查询字典获取对应值,如果没有则将你给出的一个新值插入字典。

这个方法有三个重载:

  • TValue GetOrAdd(TKey key, TValue value)
  • TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
  • TValue GetOrAdd<TArg>(TKey key, Func<TKey, TArg, TValue> valueFactory, TArg factoryArgument):
  • 第一个重载,很好理解。value是初始值,或者一个默认值。如果你用key键对字典查询,没有对应值就会把默认值加进去,并且不会扔出错误。

适用场景:我需要获取一个字典当中一个键值对。但是我并不知道我的同事有没有在其他地方,已经给这个键赋过值了。为了避免冲突,使用GetorAdd,如果其他地方出现了,那我拿到的就是同事之前赋值过的值。反之,我将一个值存入了字典,其他同事同样使用GetorAdd,可以拿取我赋的值。这既避免了重复声明,也避免了在不同的地方获取的值不一样。

  • 第二个重载稍微麻烦了一点,第一个参数还是键,第二个参数是一个接受键,返回值的委托。这个其实和第一种情况是大致一样的,只不过这里值不是确定的,被参数明确传进来的。而是传入了一个可以通过键生成值的函数。如果Get到了对应的值,那么你就会获得对应的值。如果没有获得到,就会调用委托,生成一个值,并放入字典中。

适用场景:很显然这是第一种情况的补充,如果我们的值是一个很复杂的对象,它的生成⚙需要经历很复杂的⚙生成过程。那么就应该使用这种方式,第二个参数用一个封装好的工厂方法。在不确定字典里对应的对象有没有生成的情况下,如果没有对应的对象才构造对象;反之,直接拿取其他地方已经生成的对象。

  • 第三个重载一看就觉得很麻烦,你可能觉得没什么用。事实是确实没有用,你绝大部分时候没有这种需求。第三种重载是对第二种情况的进一步补充,如果你的工厂方法产生一个值,需要不止一个参数,那么就需要使用这个重载。这个重载允许你额外传入一个任意由你指定的类型(TArg)参数。

使用场景:几乎不会被使用到。GetOrAdd方法最多支持两个参数的工厂委托。如果你的值构建过程真的很复杂,需要大量的外部参数。那GetOrAdd也起不到延迟执行的作用。

2. Coount、ISEmpty、Clear、Keys、Values、ContainsKey等

这部分跳过,和普通的字典完全一摸一样。需要额外补充说明的是,虽然ContainKey能做到检查某个键是否存在,但还是建议使用下面要将的TryGet方法。

3. TryGet和TryAdd ☄

bool TryGetValue(TKey key, out TValue value);
bool TryAdd(TKey key, TValue value);

我们先来讲讲基本不用的TryAdd方法,同样顾名思义的一个方法。传入一个键和一个值,如果键不存在,添加成功并返回true;反之,返回false。

这个方法完全被GetOrAdd和接下来要说的AddOrUpdate取代。首先我们知道GetOrAdd本身就包含了如果没有值就放一个值进去的功能,而AddOrUpdate又包含了更新的功能。所以这个方法处于一种“查无此人的状态”。

TryGet,也是顾名思义的一个方法。你需要记住的只有一点,返回值是bool,第二参数是传出(out)参数,才是你要的值。

这个更加适合处理我们之前谈到的,当你需要执行复杂的值构造。

TValue value;
if(dict.TryGet(key,vlaue))
{
///构造vlaue
}

你既可以用来处理这种复杂情况,也可以用来判断字典中是否包含特定键。
需要注意的是因为vlaue是传出参数,当key不存在时,会返回null。所以这个值的类型本身是可空的,编译器应该会给出提示,但你不应该忘记。

4. AddOrUpDate

  • AddOrUpdate(TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory)
  • AddOrUpdate(TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory)
  • AddOrUpdate<TArg>(TKey key, Func<TKey, TArg, TValue> addValueFactory, Func<TKey, TValue, TArg, TValue> updateValueFactory, TArg factoryArgument)

功能十分强大的函数,看起来就高级,使用起来令人头痛不已。和GetOrAdd方法类似,先试图向字典里添加一个键值对,如果失败,则试图对键的对应值进行更新。所以 你应该确保你真的是要更新这个值 ,如果你只是试图添加,那去用GetOrAdd,还是之前情况,对于你和同事如果维护的是同一个对象,你不确定他(她)是否创建了这个对象,那就用GetOrAdd。如果你明确这里需要一个新的值,那才应该使用AddOrUpdate。

第一种和第二种重载是同一种情况的两种不同形式。第一种重载很显然是试图对key添加值addValue。如果键已存在,则执行update委托。
第二种是先检查key是否存在,如果不存在,执行构造值委托addValueFactory。如果键存在,则执行update委托。

和GetOrAdd一样,你传入的值构造委托会被延迟执行。
我们来谈一谈关于update委托,update的委托的参数是key,oldValue,返回值是newValue。你需要告知旧值如何处理。

如果你没有什么特殊需求,给了一个key,一个value我希望的就是字典里key对应的值变成value。那么我给出一种平时常用的使用方式:

dict.AddOrUpdate(1,sunsun,(key,oldValue)=>sunsun);

sunsun是一个对象变量,1是一个键,你可以换你需要的类型。这行执行了一个很简单的事,我不管字典有没有1这个键,执行完以后1对应sunsun。

第三重载和GetOrAdd的第三重载一样,对于复杂的update委托,提供了一个额外类型的额外参数。

5. TryRemove和TryUpdate☂

  • bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue)
  • bool TryRemove(KeyValuePair<Tkey,TValue> item);
  • bool TryRemove(TKey key, out TValue value);

remove的第一重载你可能和我不一样,可能参数是一个key,这是因为NEt版本不同,所以系统库存在的一些差异。但实现的功能是一样,删除对应key的键值对。

第二重载是删除的同时将值通过传出(out)参数返回给你,同样注意如果不存在键,返回的会是null。

tryupdate这里和AddOrUpdate不同,需要注意。
第三个参数不是新值
第一个参数是key,这很好理解。第二个才是newValue,你希望更新的值。第三个值用来干什么呢?如果键存在,且旧值=comparisonValue,才会更新为newValue。就是说相比于AddOrUpdate而言,多了一步比较的过程,你只有知道你更新的键和值分别是什么,才能更新成功。如果因为异步操作,导致值变化了,那么你的更新操作会失败。
这是一种稳妥的更新方式,避免了你以为旧值是这个,所以更新了新值。其实在程序的其他地方或者你的同事依旧把旧值给改过了,因为你的擅自更新,导致其他地方出错了,的这一种情况。

Part 2 ReadOnly与Concurent结合的字典☯

在讲经验之谈之前,我们先简单讲一下ReadOlnyDictionary。这个字典类型只包含keys、Values、count一些简单的成员。你无法使用add、clear等任何方式修改字典的键值对。键值对在初始化后就无法被修改。

顺带再讲讲readOnly关键字,也许你没有使用过这个关键字,但能看懂。

  public readonly int a=1;

我们在类中定义了一个成员属性a,他的值永远为1不能修改。
这对于非引用类型毫无意义,对于引用类型十分有用。可以防止你以任何方式对这个成员对象重新分配(new)一个新对象,而不会影响你对这个对象自身包含的成员的使用和修改。

明白了以上的内容,我将告诉你如何写出一个线程安全的字典。

public class MyDictionary
{
    private readonly ConcurrentDictionary<string, string> _myDicttionary = new ConcurrentDictionary<string, string>();

    public IReadOnlyDictionary<string, string> TheDicttionary=> _myDicttionary;

    public void AddorUpDate(String key, String value) {
        _myDicttionary.AddOrUpdate(key, value, (key, oldValue) => value);
    }

    public void GetOrAdd(string key, string value) {

        _myDicttionary.GetOrAdd(key, value);
    }
    //其他你需要的函数
}

我们将 ConcurrentDictionary进行readonly并且私有,任何人无法再分配一个新的对象给这个字典,如果你使用依赖注入,可以进一步实现全局单例模式。我们用IReadOnlyDictionary接口封装我们的线程安全字典,这样类外无法直接通过扩展方法修改这个字典中的键值对。下面几个函数是我们保留的修改接口。在类内可以正常修改字典的键值对,如果你需要使用之前介绍的其他扩展方法,可以自己封装一个。
并且封装的修改函数相比于直接调用扩展函数的优势在于,你可以在函数内写一些逻辑,阻止你不希望的修改,也可以在方法当中加入同步的逻辑,保证线程安全。

C#字典多线程读写

C#字典多线程读写

 字典是全局多个线程共享的,其中一个线程负责更新字典的数据,其他线程负责读取遍历字典

有多个实现方式

  1. 给字典自身加锁
  2. 增加一个变量o锁住需要的部分
  3. 使用ConcurrentDictionary

其中第1和3的缺点:整个字典带锁,在更新字典的同时无法同时遍历字典,每次更新字典时需要判断旧数据是否有需要删除的增加了额外的工作量,违背了我们使用多线程的初衷

 

 

class TaskTest : IHostedService
    {
        private System.Timers.Timer timer;
        private static object o = new object();
        private static int inTimer = 0;

        private static Dictionary<string, int> dic = new Dictionary<string, int>();

        private void doTask(object source, System.Timers.ElapsedEventArgs e)
        {

            if (Interlocked.Exchange(ref inTimer, 1) != 0)
            {
                Console.WriteLine("拒绝进入");
                return;
            }

            try
            {
                Console.WriteLine($"定时任务:{DateTime.Now}");
                lock (o)
                {//拿到锁之后在遍历,防止在遍历的时候其他线程修改字典
                    dic.Add("zhulangren", 100);
                    foreach (KeyValuePair<string, int> kv in dic)
                    {
                        Console.WriteLine($"key:{kv.Key},value:{kv.Value}");
                    }
                    Console.WriteLine($"定时任务锁:{DateTime.Now}");
                }
                Console.WriteLine($"定时任务结束:{DateTime.Now}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"异常:{ex.ToString()}");
            }
            finally
            {
                Interlocked.Exchange(ref inTimer, 0);

            }


        }
        public Task StartAsync(CancellationToken cancellationToken)
        {
            timer = new System.Timers.Timer(1000);//
            timer.Elapsed += new System.Timers.ElapsedEventHandler(doTask);//到达时间的时候执行事件;
            timer.AutoReset = true;//设置是执行一次(false)还是一直执行(true);
            timer.Enabled = true;


            Task.Run(() =>
            {
                while (true)
                {
                    Console.WriteLine($"非定时任务:{DateTime.Now}");
                    Dictionary<string, int> dic_tmp = new Dictionary<string, int>();
                    for (int i = 0; i < 20; i++)
                    {
                        dic_tmp.Add($"abc{DateTime.Now}{i}", i);
                    }

                    lock (o)
                    {//只锁住赋值部分不锁整个字典的好处是,数值填充部分往往会占用大量时间,这样可以节省一部分时间给其它线程遍历数据
                        Console.WriteLine($"非定时任务锁开始:{DateTime.Now}");

                        dic = dic_tmp;
                        Thread.Sleep(5000);
                        Console.WriteLine($"非定时任务锁结束:{DateTime.Now}");

                    };
                }

            });
            return Task.CompletedTask;



        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            return Task.CompletedTask;
        }
    }

代码运行结果:

非定时任务:2021/5/8 15:22:28
非定时任务锁开始:2021/5/8 15:22:28
定时任务:2021/5/8 15:22:29
拒绝进入
拒绝进入
拒绝进入
拒绝进入
非定时任务锁结束:2021/5/8 15:22:33
非定时任务:2021/5/8 15:22:33
key:abc2021/5/8 15:22:280,value:0
key:abc2021/5/8 15:22:281,value:1
key:abc2021/5/8 15:22:282,value:2
key:abc2021/5/8 15:22:283,value:3
key:abc2021/5/8 15:22:284,value:4
key:abc2021/5/8 15:22:285,value:5
key:abc2021/5/8 15:22:286,value:6
key:abc2021/5/8 15:22:287,value:7
key:abc2021/5/8 15:22:288,value:8
key:abc2021/5/8 15:22:289,value:9
key:abc2021/5/8 15:22:2810,value:10
key:abc2021/5/8 15:22:2811,value:11
key:abc2021/5/8 15:22:2812,value:12
key:abc2021/5/8 15:22:2813,value:13
key:abc2021/5/8 15:22:2814,value:14
key:abc2021/5/8 15:22:2815,value:15
key:abc2021/5/8 15:22:2816,value:16
key:abc2021/5/8 15:22:2817,value:17
key:abc2021/5/8 15:22:2818,value:18
key:abc2021/5/8 15:22:2819,value:19
key:zhulangren,value:100
定时任务锁:2021/5/8 15:22:33
非定时任务锁开始:2021/5/8 15:22:33
定时任务结束:2021/5/8 15:22:33
定时任务:2021/5/8 15:22:34
拒绝进入
拒绝进入
拒绝进入
拒绝进入
非定时任务锁结束:2021/5/8 15:22:38
非定时任务:2021/5/8 15:22:38
key:abc2021/5/8 15:22:330,value:0
key:abc2021/5/8 15:22:331,value:1
key:abc2021/5/8 15:22:332,value:2
key:abc2021/5/8 15:22:333,value:3
key:abc2021/5/8 15:22:334,value:4
key:abc2021/5/8 15:22:335,value:5
key:abc2021/5/8 15:22:336,value:6
key:abc2021/5/8 15:22:337,value:7
key:abc2021/5/8 15:22:338,value:8
key:abc2021/5/8 15:22:339,value:9
key:abc2021/5/8 15:22:3310,value:10
key:abc2021/5/8 15:22:3311,value:11
key:abc2021/5/8 15:22:3312,value:12
key:abc2021/5/8 15:22:3313,value:13
key:abc2021/5/8 15:22:3314,value:14
key:abc2021/5/8 15:22:3315,value:15
key:abc2021/5/8 15:22:3316,value:16
key:abc2021/5/8 15:22:3317,value:17
key:abc2021/5/8 15:22:3318,value:18
key:abc2021/5/8 15:22:3319,value:19
key:zhulangren,value:100
定时任务锁:2021/5/8 15:22:38
定时任务结束:2021/5/8 15:22:38
非定时任务锁开始:2021/5/8 15:22:38
定时任务:2021/5/8 15:22:39

 

C语言随笔_printf输出多行 - 知乎

C语言随笔_printf输出多行 - 知乎

版权声明:本文为博主原创文章,转载时请注明文章来源,否则视为侵权,并按法律程序处理!


想在printf中,输出多行数据,如果写成下面这样:
printf("line 1\n
line 2\n
line 3\n");
编译器会报错“error C2001: newline in constant”。

可以这样写:
printf("line 1\
line 2\
line 3\n");

或者

printf("line 1"
"line 2"
"line 3\n");















Go 语言中 Print,Println 和 Printf 的区别(八)

Go 语言中 Print,Println 和 Printf 的区别(八)

尊嘟假嘟,成都程序员也 996 吗?

Print 和 Println

这两个打印方式类似,只在格式上有区别

1. Println 打印的每一项之间都会有空行,Print 没有,例如:

fmt.Println("go","python","php","javascript") // go python php javascript
fmt.Print("go","python","php","javascript") // gopythonphpjavascript

 

2. Println 会自动换行,Print 不会,例如:

fmt.Println("hello")
fmt.Println("world")

// hello
// world



fmt.Print("hello")
fmt.Print("world")

// helloworld

 

Println 和 Printf

Printf 是格式化输出,在很多场景下比 Println 更方便,举个例子:

func main() {
    a:=10
    b:=20
    c:=30
    fmt.Println("a=", a , ",b=" , b , ",c=" , c)
    fmt.Printf("a=%d,b=%d,c=%d" , a , b , c)
}

 % d 是占位符,表示数字的十进制表示。Printf 中的占位符与后面的数字变量一一对应。更多的占位符参考:http://docscn.studygolang.com/pkg/fmt/

关于pprint字典多行的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于.NET进阶:readOnly、 Concurrent字典,如何写出线程安全字典、C#字典多线程读写、C语言随笔_printf输出多行 - 知乎、Go 语言中 Print,Println 和 Printf 的区别(八)等相关内容,可以在本站寻找。

本文标签: